back to topotato report
topotato coverage report
Current view: top level - pimd - pim_upstream.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 536 925 57.9 %
Date: 2023-02-24 14:41:08 Functions: 52 69 75.4 %

          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 "log.h"
      23             : #include "zclient.h"
      24             : #include "memory.h"
      25             : #include "thread.h"
      26             : #include "linklist.h"
      27             : #include "vty.h"
      28             : #include "plist.h"
      29             : #include "hash.h"
      30             : #include "jhash.h"
      31             : #include "wheel.h"
      32             : #include "network.h"
      33             : 
      34             : #include "pimd.h"
      35             : #include "pim_pim.h"
      36             : #include "pim_str.h"
      37             : #include "pim_time.h"
      38             : #include "pim_iface.h"
      39             : #include "pim_join.h"
      40             : #include "pim_zlookup.h"
      41             : #include "pim_upstream.h"
      42             : #include "pim_ifchannel.h"
      43             : #include "pim_neighbor.h"
      44             : #include "pim_rpf.h"
      45             : #include "pim_zebra.h"
      46             : #include "pim_oil.h"
      47             : #include "pim_macro.h"
      48             : #include "pim_rp.h"
      49             : #include "pim_register.h"
      50             : #include "pim_msdp.h"
      51             : #include "pim_jp_agg.h"
      52             : #include "pim_nht.h"
      53             : #include "pim_ssm.h"
      54             : #include "pim_vxlan.h"
      55             : #include "pim_mlag.h"
      56             : 
      57             : static void join_timer_stop(struct pim_upstream *up);
      58             : static void
      59             : pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
      60             : static bool pim_upstream_sg_running_proc(struct pim_upstream *up);
      61             : 
      62             : /*
      63             :  * A (*,G) or a (*,*) is going away
      64             :  * remove the parent pointer from
      65             :  * those pointing at us
      66             :  */
      67          15 : static void pim_upstream_remove_children(struct pim_instance *pim,
      68             :                                          struct pim_upstream *up)
      69             : {
      70          15 :         struct pim_upstream *child;
      71             : 
      72          15 :         if (!up->sources)
      73             :                 return;
      74             : 
      75           5 :         while (!list_isempty(up->sources)) {
      76           2 :                 child = listnode_head(up->sources);
      77           2 :                 listnode_delete(up->sources, child);
      78           2 :                 if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(child->flags)) {
      79           0 :                         PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(child->flags);
      80           0 :                         child = pim_upstream_del(pim, child, __func__);
      81             :                 }
      82           0 :                 if (child) {
      83           2 :                         child->parent = NULL;
      84           2 :                         if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
      85           0 :                                 pim_upstream_mroute_iif_update(
      86             :                                                 child->channel_oil,
      87             :                                                 __func__);
      88             :                 }
      89             :         }
      90           3 :         list_delete(&up->sources);
      91             : }
      92             : 
      93             : /*
      94             :  * A (*,G) or a (*,*) is being created
      95             :  * Find the children that would point
      96             :  * at us.
      97             :  */
      98          15 : static void pim_upstream_find_new_children(struct pim_instance *pim,
      99             :                                            struct pim_upstream *up)
     100             : {
     101          15 :         struct pim_upstream *child;
     102             : 
     103          15 :         if (!pim_addr_is_any(up->sg.src) && !pim_addr_is_any(up->sg.grp))
     104             :                 return;
     105             : 
     106           3 :         if (pim_addr_is_any(up->sg.src) && pim_addr_is_any(up->sg.grp))
     107             :                 return;
     108             : 
     109          20 :         frr_each (rb_pim_upstream, &pim->upstream_head, child) {
     110           7 :                 if (!pim_addr_is_any(up->sg.grp) &&
     111           7 :                     !pim_addr_cmp(child->sg.grp, up->sg.grp) && (child != up)) {
     112           0 :                         child->parent = up;
     113           0 :                         listnode_add_sort(up->sources, child);
     114           0 :                         if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
     115           0 :                                 pim_upstream_mroute_iif_update(
     116             :                                                 child->channel_oil,
     117             :                                                 __func__);
     118             :                 }
     119             :         }
     120             : }
     121             : 
     122             : /*
     123             :  * If we have a (*,*) || (S,*) there is no parent
     124             :  * If we have a (S,G), find the (*,G)
     125             :  * If we have a (*,G), find the (*,*)
     126             :  */
     127          15 : static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim,
     128             :                                                      struct pim_upstream *child)
     129             : {
     130          15 :         pim_sgaddr any = child->sg;
     131          15 :         struct pim_upstream *up = NULL;
     132             : 
     133             :         // (S,G)
     134          15 :         if (!pim_addr_is_any(child->sg.src) &&
     135          12 :             !pim_addr_is_any(child->sg.grp)) {
     136          12 :                 any.src = PIMADDR_ANY;
     137          12 :                 up = pim_upstream_find(pim, &any);
     138             : 
     139          12 :                 if (up)
     140           2 :                         listnode_add(up->sources, child);
     141             : 
     142             :                 /*
     143             :                  * In case parent is MLAG entry copy the data to child
     144             :                  */
     145           2 :                 if (up && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) {
     146           0 :                         PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(child->flags);
     147           0 :                         if (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags))
     148           0 :                                 PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(child->flags);
     149             :                         else
     150           0 :                                 PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(
     151             :                                         child->flags);
     152             :                 }
     153             : 
     154          12 :                 return up;
     155             :         }
     156             : 
     157             :         return NULL;
     158             : }
     159             : 
     160          15 : static void upstream_channel_oil_detach(struct pim_upstream *up)
     161             : {
     162          15 :         struct channel_oil *channel_oil = up->channel_oil;
     163             : 
     164          15 :         if (channel_oil) {
     165             :                 /* Detaching from channel_oil, channel_oil may exist post del,
     166             :                    but upstream would not keep reference of it
     167             :                  */
     168          15 :                 channel_oil->up = NULL;
     169          15 :                 up->channel_oil = NULL;
     170             : 
     171             :                 /* attempt to delete channel_oil; if channel_oil is being held
     172             :                  * because of other references cleanup info such as "Mute"
     173             :                  * inferred from the parent upstream
     174             :                  */
     175          15 :                 pim_channel_oil_upstream_deref(channel_oil);
     176             :         }
     177             : 
     178          15 : }
     179             : 
     180          18 : static void pim_upstream_timers_stop(struct pim_upstream *up)
     181             : {
     182          18 :         THREAD_OFF(up->t_ka_timer);
     183          18 :         THREAD_OFF(up->t_rs_timer);
     184          18 :         THREAD_OFF(up->t_msdp_reg_timer);
     185          18 :         THREAD_OFF(up->t_join_timer);
     186          18 : }
     187             : 
     188          19 : struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
     189             :                                       struct pim_upstream *up, const char *name)
     190             : {
     191          19 :         struct listnode *node, *nnode;
     192          19 :         struct pim_ifchannel *ch;
     193          19 :         bool notify_msdp = false;
     194             : 
     195          19 :         if (PIM_DEBUG_PIM_TRACE)
     196           0 :                 zlog_debug(
     197             :                         "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)",
     198             :                         __func__, name, up->sg_str, pim->vrf->name,
     199             :                         up->ref_count, up->flags,
     200             :                         up->channel_oil->oil_ref_count);
     201             : 
     202          19 :          assert(up->ref_count > 0);
     203             : 
     204          19 :         --up->ref_count;
     205             : 
     206          19 :         if (up->ref_count >= 1)
     207             :                 return up;
     208             : 
     209          15 :         if (PIM_DEBUG_TRACE)
     210           2 :                 zlog_debug("pim_upstream free vrf:%s %s flags 0x%x",
     211             :                            pim->vrf->name, up->sg_str, up->flags);
     212             : 
     213          15 :         if (pim_up_mlag_is_local(up))
     214           0 :                 pim_mlag_up_local_del(pim, up);
     215             : 
     216          15 :         pim_upstream_timers_stop(up);
     217             : 
     218          15 :         if (up->join_state == PIM_UPSTREAM_JOINED) {
     219           9 :                 pim_jp_agg_single_upstream_send(&up->rpf, up, 0);
     220             : 
     221           9 :                 if (pim_addr_is_any(up->sg.src)) {
     222             :                         /* if a (*, G) entry in the joined state is being
     223             :                          * deleted we
     224             :                          * need to notify MSDP */
     225          13 :                         notify_msdp = true;
     226             :                 }
     227             :         }
     228             : 
     229          15 :         join_timer_stop(up);
     230          15 :         pim_jp_agg_upstream_verification(up, false);
     231          15 :         up->rpf.source_nexthop.interface = NULL;
     232             : 
     233          15 :         if (!pim_addr_is_any(up->sg.src)) {
     234          12 :                 if (pim->upstream_sg_wheel)
     235          12 :                         wheel_remove_item(pim->upstream_sg_wheel, up);
     236             :                 notify_msdp = true;
     237             :         }
     238             : 
     239          15 :         pim_mroute_del(up->channel_oil, __func__);
     240          15 :         upstream_channel_oil_detach(up);
     241             : 
     242          40 :         for (ALL_LIST_ELEMENTS(up->ifchannels, node, nnode, ch))
     243          10 :                 pim_ifchannel_delete(ch);
     244          15 :         list_delete(&up->ifchannels);
     245             : 
     246          15 :         pim_upstream_remove_children(pim, up);
     247          15 :         if (up->sources)
     248           0 :                 list_delete(&up->sources);
     249             : 
     250          15 :         if (up->parent && up->parent->sources)
     251           0 :                 listnode_delete(up->parent->sources, up);
     252          15 :         up->parent = NULL;
     253             : 
     254          15 :         rb_pim_upstream_del(&pim->upstream_head, up);
     255             : 
     256          15 :         if (notify_msdp) {
     257           7 :                 pim_msdp_up_del(pim, &up->sg);
     258             :         }
     259             : 
     260             :         /* When RP gets deleted, pim_rp_del() deregister addr with Zebra NHT
     261             :          * and assign up->upstream_addr as INADDR_ANY.
     262             :          * So before de-registering the upstream address, check if is not equal
     263             :          * to INADDR_ANY. This is done in order to avoid de-registering for
     264             :          * 255.255.255.255 which is maintained for some reason..
     265             :          */
     266          15 :         if (!pim_addr_is_any(up->upstream_addr)) {
     267             :                 /* Deregister addr with Zebra NHT */
     268          14 :                 if (PIM_DEBUG_PIM_TRACE)
     269           0 :                         zlog_debug(
     270             :                                 "%s: Deregister upstream %s addr %pPA with Zebra NHT",
     271             :                                 __func__, up->sg_str, &up->upstream_addr);
     272          14 :                 pim_delete_tracked_nexthop(pim, up->upstream_addr, up, NULL);
     273             :         }
     274             : 
     275          15 :         XFREE(MTYPE_PIM_UPSTREAM, up);
     276             : 
     277          15 :         return NULL;
     278             : }
     279             : 
     280           6 : void pim_upstream_send_join(struct pim_upstream *up)
     281             : {
     282           6 :         if (!up->rpf.source_nexthop.interface) {
     283           0 :                 if (PIM_DEBUG_PIM_TRACE)
     284           0 :                         zlog_debug("%s: up %s RPF is not present", __func__,
     285             :                                    up->sg_str);
     286           0 :                 return;
     287             :         }
     288             : 
     289           6 :         if (PIM_DEBUG_PIM_TRACE) {
     290           0 :                 zlog_debug("%s: RPF'%s=%pPA(%s) for Interface %s", __func__,
     291             :                            up->sg_str, &up->rpf.rpf_addr,
     292             :                            pim_upstream_state2str(up->join_state),
     293             :                            up->rpf.source_nexthop.interface->name);
     294           0 :                 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
     295           0 :                         zlog_debug("%s: can't send join upstream: RPF'%s=%pPA",
     296             :                                    __func__, up->sg_str, &up->rpf.rpf_addr);
     297             :                         /* warning only */
     298             :                 }
     299             :         }
     300             : 
     301             :         /* send Join(S,G) to the current upstream neighbor */
     302           6 :         pim_jp_agg_single_upstream_send(&up->rpf, up, 1 /* join */);
     303             : }
     304             : 
     305           0 : static void on_join_timer(struct thread *t)
     306             : {
     307           0 :         struct pim_upstream *up;
     308             : 
     309           0 :         up = THREAD_ARG(t);
     310             : 
     311           0 :         if (!up->rpf.source_nexthop.interface) {
     312           0 :                 if (PIM_DEBUG_PIM_TRACE)
     313           0 :                         zlog_debug("%s: up %s RPF is not present", __func__,
     314             :                                    up->sg_str);
     315           0 :                 return;
     316             :         }
     317             : 
     318             :         /*
     319             :          * In the case of a HFR we will not ahve anyone to send this to.
     320             :          */
     321           0 :         if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
     322             :                 return;
     323             : 
     324             :         /*
     325             :          * Don't send the join if the outgoing interface is a loopback
     326             :          * But since this might change leave the join timer running
     327             :          */
     328           0 :         if (up->rpf.source_nexthop
     329           0 :                     .interface && !if_is_loopback(up->rpf.source_nexthop.interface))
     330           0 :                 pim_upstream_send_join(up);
     331             : 
     332           0 :         join_timer_start(up);
     333             : }
     334             : 
     335          16 : static void join_timer_stop(struct pim_upstream *up)
     336             : {
     337          16 :         struct pim_neighbor *nbr = NULL;
     338             : 
     339          16 :         THREAD_OFF(up->t_join_timer);
     340             : 
     341          16 :         if (up->rpf.source_nexthop.interface)
     342          15 :                 nbr = pim_neighbor_find(up->rpf.source_nexthop.interface,
     343             :                                         up->rpf.rpf_addr);
     344             : 
     345          15 :         if (nbr)
     346           3 :                 pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
     347             : 
     348          16 :         pim_jp_agg_upstream_verification(up, false);
     349          16 : }
     350             : 
     351          10 : void join_timer_start(struct pim_upstream *up)
     352             : {
     353          10 :         struct pim_neighbor *nbr = NULL;
     354             : 
     355          10 :         if (up->rpf.source_nexthop.interface) {
     356          10 :                 nbr = pim_neighbor_find(up->rpf.source_nexthop.interface,
     357             :                                         up->rpf.rpf_addr);
     358             : 
     359          10 :                 if (PIM_DEBUG_PIM_EVENTS) {
     360           0 :                         zlog_debug(
     361             :                                 "%s: starting %d sec timer for upstream (S,G)=%s",
     362             :                                 __func__, router->t_periodic, up->sg_str);
     363             :                 }
     364             :         }
     365             : 
     366          10 :         if (nbr)
     367           5 :                 pim_jp_agg_add_group(nbr->upstream_jp_agg, up, 1, nbr);
     368             :         else {
     369           5 :                 THREAD_OFF(up->t_join_timer);
     370           5 :                 thread_add_timer(router->master, on_join_timer, up,
     371             :                                  router->t_periodic, &up->t_join_timer);
     372             :         }
     373          10 :         pim_jp_agg_upstream_verification(up, true);
     374          10 : }
     375             : 
     376             : /*
     377             :  * This is only called when we are switching the upstream
     378             :  * J/P from one neighbor to another
     379             :  *
     380             :  * As such we need to remove from the old list and
     381             :  * add to the new list.
     382             :  */
     383           0 : void pim_upstream_join_timer_restart(struct pim_upstream *up,
     384             :                                      struct pim_rpf *old)
     385             : {
     386             :         // THREAD_OFF(up->t_join_timer);
     387           0 :         join_timer_start(up);
     388           0 : }
     389             : 
     390           0 : static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
     391             :                                                  int interval_msec)
     392             : {
     393           0 :         if (PIM_DEBUG_PIM_EVENTS) {
     394           0 :                 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
     395             :                            __func__, interval_msec, up->sg_str);
     396             :         }
     397             : 
     398           0 :         THREAD_OFF(up->t_join_timer);
     399           0 :         thread_add_timer_msec(router->master, on_join_timer, up, interval_msec,
     400             :                               &up->t_join_timer);
     401           0 : }
     402             : 
     403           0 : void pim_update_suppress_timers(uint32_t suppress_time)
     404             : {
     405           0 :         struct pim_instance *pim;
     406           0 :         struct vrf *vrf;
     407           0 :         unsigned int old_rp_ka_time;
     408             : 
     409             :         /* stash the old one so we know which values were manually configured */
     410           0 :         old_rp_ka_time =  (3 * router->register_suppress_time
     411           0 :                            + router->register_probe_time);
     412           0 :         router->register_suppress_time = suppress_time;
     413             : 
     414           0 :         RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
     415           0 :                 pim = vrf->info;
     416           0 :                 if (!pim)
     417           0 :                         continue;
     418             : 
     419             :                 /* Only adjust if not manually configured */
     420           0 :                 if (pim->rp_keep_alive_time == old_rp_ka_time)
     421           0 :                         pim->rp_keep_alive_time = PIM_RP_KEEPALIVE_PERIOD;
     422             :         }
     423           0 : }
     424             : 
     425           0 : void pim_upstream_join_suppress(struct pim_upstream *up, pim_addr rpf,
     426             :                                 int holdtime)
     427             : {
     428           0 :         long t_joinsuppress_msec;
     429           0 :         long join_timer_remain_msec = 0;
     430           0 :         struct pim_neighbor *nbr = NULL;
     431             : 
     432           0 :         if (!up->rpf.source_nexthop.interface) {
     433           0 :                 if (PIM_DEBUG_PIM_TRACE)
     434           0 :                         zlog_debug("%s: up %s RPF is not present", __func__,
     435             :                                    up->sg_str);
     436           0 :                 return;
     437             :         }
     438             : 
     439           0 :         t_joinsuppress_msec =
     440           0 :                 MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
     441             :                     1000 * holdtime);
     442             : 
     443           0 :         if (up->t_join_timer)
     444           0 :                 join_timer_remain_msec =
     445           0 :                         pim_time_timer_remain_msec(up->t_join_timer);
     446             :         else {
     447             :                 /* Remove it from jp agg from the nbr for suppression */
     448           0 :                 nbr = pim_neighbor_find(up->rpf.source_nexthop.interface,
     449             :                                         up->rpf.rpf_addr);
     450           0 :                 if (nbr) {
     451           0 :                         join_timer_remain_msec =
     452           0 :                                 pim_time_timer_remain_msec(nbr->jp_timer);
     453             :                 }
     454             :         }
     455             : 
     456           0 :         if (PIM_DEBUG_PIM_TRACE)
     457           0 :                 zlog_debug(
     458             :                         "%s %s: detected Join%s to RPF'(S,G)=%pPA: join_timer=%ld msec t_joinsuppress=%ld msec",
     459             :                         __FILE__, __func__, up->sg_str, &rpf,
     460             :                         join_timer_remain_msec, t_joinsuppress_msec);
     461             : 
     462           0 :         if (join_timer_remain_msec < t_joinsuppress_msec) {
     463           0 :                 if (PIM_DEBUG_PIM_TRACE) {
     464           0 :                         zlog_debug(
     465             :                                 "%s %s: suppressing Join(S,G)=%s for %ld msec",
     466             :                                 __FILE__, __func__, up->sg_str,
     467             :                                 t_joinsuppress_msec);
     468             :                 }
     469             : 
     470           0 :                 if (nbr)
     471           0 :                         pim_jp_agg_remove_group(nbr->upstream_jp_agg, up, nbr);
     472             : 
     473           0 :                 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
     474             :         }
     475             : }
     476             : 
     477           0 : void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
     478             :                                                     struct pim_upstream *up)
     479             : {
     480           0 :         long join_timer_remain_msec;
     481           0 :         int t_override_msec;
     482             : 
     483           0 :         if (!up->rpf.source_nexthop.interface) {
     484           0 :                 if (PIM_DEBUG_PIM_TRACE)
     485           0 :                         zlog_debug("%s: up %s RPF is not present", __func__,
     486             :                                    up->sg_str);
     487           0 :                 return;
     488             :         }
     489             : 
     490           0 :         t_override_msec =
     491           0 :                 pim_if_t_override_msec(up->rpf.source_nexthop.interface);
     492             : 
     493           0 :         if (up->t_join_timer) {
     494           0 :                 join_timer_remain_msec =
     495           0 :                         pim_time_timer_remain_msec(up->t_join_timer);
     496             :         } else {
     497             :                 /* upstream join tracked with neighbor jp timer */
     498           0 :                 struct pim_neighbor *nbr;
     499             : 
     500           0 :                 nbr = pim_neighbor_find(up->rpf.source_nexthop.interface,
     501             :                                         up->rpf.rpf_addr);
     502           0 :                 if (nbr)
     503           0 :                         join_timer_remain_msec =
     504           0 :                                 pim_time_timer_remain_msec(nbr->jp_timer);
     505             :                 else
     506             :                         /* Manipulate such that override takes place */
     507           0 :                         join_timer_remain_msec = t_override_msec + 1;
     508             :         }
     509             : 
     510           0 :         if (PIM_DEBUG_PIM_TRACE)
     511           0 :                 zlog_debug(
     512             :                         "%s: to RPF'%s=%pPA: join_timer=%ld msec t_override=%d msec",
     513             :                         debug_label, up->sg_str, &up->rpf.rpf_addr,
     514             :                         join_timer_remain_msec, t_override_msec);
     515             : 
     516           0 :         if (join_timer_remain_msec > t_override_msec) {
     517           0 :                 if (PIM_DEBUG_PIM_TRACE) {
     518           0 :                         zlog_debug(
     519             :                                 "%s: decreasing (S,G)=%s join timer to t_override=%d msec",
     520             :                                 debug_label, up->sg_str, t_override_msec);
     521             :                 }
     522             : 
     523           0 :                 pim_upstream_join_timer_restart_msec(up, t_override_msec);
     524             :         }
     525             : }
     526             : 
     527           3 : static void forward_on(struct pim_upstream *up)
     528             : {
     529           3 :         struct listnode *chnode;
     530           3 :         struct listnode *chnextnode;
     531           3 :         struct pim_ifchannel *ch = NULL;
     532             : 
     533             :         /* scan (S,G) state */
     534           6 :         for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
     535           0 :                 if (pim_macro_chisin_oiflist(ch))
     536           0 :                         pim_forward_start(ch);
     537             : 
     538             :         } /* scan iface channel list */
     539           3 : }
     540             : 
     541           1 : static void forward_off(struct pim_upstream *up)
     542             : {
     543           1 :         struct listnode *chnode;
     544           1 :         struct listnode *chnextnode;
     545           1 :         struct pim_ifchannel *ch;
     546             : 
     547             :         /* scan per-interface (S,G) state */
     548           3 :         for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
     549             : 
     550           1 :                 pim_forward_stop(ch);
     551             : 
     552             :         } /* scan iface channel list */
     553           1 : }
     554             : 
     555          14 : int pim_upstream_could_register(struct pim_upstream *up)
     556             : {
     557          14 :         struct pim_interface *pim_ifp = NULL;
     558             : 
     559             :         /* FORCE_PIMREG is a generic flag to let an app like VxLAN-AA register
     560             :          * a source on an upstream entry even if the source is not directly
     561             :          * connected on the IIF.
     562             :          */
     563          14 :         if (PIM_UPSTREAM_FLAG_TEST_FORCE_PIMREG(up->flags))
     564             :                 return 1;
     565             : 
     566          14 :         if (up->rpf.source_nexthop.interface)
     567          14 :                 pim_ifp = up->rpf.source_nexthop.interface->info;
     568             :         else {
     569           0 :                 if (PIM_DEBUG_PIM_TRACE)
     570          14 :                         zlog_debug("%s: up %s RPF is not present", __func__,
     571             :                                    up->sg_str);
     572             :         }
     573             : 
     574          14 :         if (pim_ifp && PIM_I_am_DR(pim_ifp)
     575          13 :             && pim_if_connected_to_source(up->rpf.source_nexthop.interface,
     576             :                                           up->sg.src))
     577             :                 return 1;
     578             : 
     579             :         return 0;
     580             : }
     581             : 
     582             : /* Source registration is suppressed for SSM groups. When the SSM range changes
     583             :  * we re-revaluate register setup for existing upstream entries */
     584           0 : void pim_upstream_register_reevaluate(struct pim_instance *pim)
     585             : {
     586           0 :         struct pim_upstream *up;
     587             : 
     588           0 :         frr_each (rb_pim_upstream, &pim->upstream_head, up) {
     589             :                 /* If FHR is set CouldRegister is True. Also check if the flow
     590             :                  * is actually active; if it is not kat setup will trigger
     591             :                  * source
     592             :                  * registration whenever the flow becomes active. */
     593           0 :                 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
     594           0 :                         !pim_upstream_is_kat_running(up))
     595           0 :                         continue;
     596             : 
     597           0 :                 if (pim_is_grp_ssm(pim, up->sg.grp)) {
     598             :                         /* clear the register state  for SSM groups */
     599           0 :                         if (up->reg_state != PIM_REG_NOINFO) {
     600           0 :                                 if (PIM_DEBUG_PIM_EVENTS)
     601           0 :                                         zlog_debug(
     602             :                                                 "Clear register for %s as G is now SSM",
     603             :                                                 up->sg_str);
     604             :                                 /* remove regiface from the OIL if it is there*/
     605           0 :                                 pim_channel_del_oif(up->channel_oil,
     606             :                                                     pim->regiface,
     607             :                                                     PIM_OIF_FLAG_PROTO_PIM,
     608             :                                                         __func__);
     609           0 :                                 up->reg_state = PIM_REG_NOINFO;
     610             :                         }
     611             :                 } else {
     612             :                         /* register ASM sources with the RP */
     613           0 :                         if (up->reg_state == PIM_REG_NOINFO) {
     614           0 :                                 if (PIM_DEBUG_PIM_EVENTS)
     615           0 :                                         zlog_debug(
     616             :                                                 "Register %s as G is now ASM",
     617             :                                                 up->sg_str);
     618           0 :                                 pim_channel_add_oif(up->channel_oil,
     619             :                                                     pim->regiface,
     620             :                                                     PIM_OIF_FLAG_PROTO_PIM,
     621             :                                                         __func__);
     622           0 :                                 up->reg_state = PIM_REG_JOIN;
     623             :                         }
     624             :                 }
     625             :         }
     626           0 : }
     627             : 
     628             : /* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
     629             :  * forward a S -
     630             :  * 1. along the SPT if SPTbit is set
     631             :  * 2. and along the RPT if SPTbit is not set
     632             :  * If forwarding is hw accelerated i.e. control and dataplane components
     633             :  * are separate you may not be able to reliably set SPT bit on intermediate
     634             :  * routers while still forwarding on the (S,G,rpt).
     635             :  *
     636             :  * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
     637             :  * criteria to decide between using the RPT vs. SPT for forwarding.
     638             :  */
     639          26 : void pim_upstream_update_use_rpt(struct pim_upstream *up,
     640             :                         bool update_mroute)
     641             : {
     642          26 :         bool old_use_rpt;
     643          26 :         bool new_use_rpt;
     644             : 
     645          26 :         if (pim_addr_is_any(up->sg.src))
     646             :                 return;
     647             : 
     648          22 :         old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
     649             : 
     650             :         /* We will use the SPT (IIF=RPF_interface(S) if -
     651             :          * 1. We have decided to join the SPT
     652             :          * 2. We are FHR
     653             :          * 3. Source is directly connected
     654             :          * 4. We are RP (parent's IIF is lo or vrf-device)
     655             :          * In all other cases the source will stay along the RPT and
     656             :          * IIF=RPF_interface(RP).
     657             :          */
     658          22 :         if (up->join_state == PIM_UPSTREAM_JOINED ||
     659          23 :                         PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
     660          10 :                         pim_if_connected_to_source(
     661             :                                 up->rpf.source_nexthop.interface,
     662          10 :                                 up->sg.src) ||
     663             :                         /* XXX - need to switch this to a more efficient
     664             :                          * lookup API
     665             :                          */
     666          10 :                         I_am_RP(up->pim, up->sg.grp))
     667             :                 /* use SPT */
     668          15 :                 PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags);
     669             :         else
     670             :                 /* use RPT */
     671           7 :                 PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags);
     672             : 
     673          22 :         new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
     674          22 :         if (old_use_rpt != new_use_rpt) {
     675          13 :                 if (PIM_DEBUG_PIM_EVENTS)
     676           0 :                         zlog_debug("%s switched from %s to %s",
     677             :                                         up->sg_str,
     678             :                                         old_use_rpt?"RPT":"SPT",
     679             :                                         new_use_rpt?"RPT":"SPT");
     680          13 :                 if (update_mroute)
     681           7 :                         pim_upstream_mroute_add(up->channel_oil, __func__);
     682             :         }
     683             : }
     684             : 
     685             : /* some events like RP change require re-evaluation of SGrpt across
     686             :  * all groups
     687             :  */
     688           5 : void pim_upstream_reeval_use_rpt(struct pim_instance *pim)
     689             : {
     690           5 :         struct pim_upstream *up;
     691             : 
     692          10 :         frr_each (rb_pim_upstream, &pim->upstream_head, up) {
     693           0 :                 if (pim_addr_is_any(up->sg.src))
     694           0 :                         continue;
     695             : 
     696           0 :                 pim_upstream_update_use_rpt(up, true /*update_mroute*/);
     697             :         }
     698           5 : }
     699             : 
     700          21 : void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
     701             :                          enum pim_upstream_state new_state)
     702             : {
     703          21 :         enum pim_upstream_state old_state = up->join_state;
     704             : 
     705          21 :         if (pim_addr_is_any(up->upstream_addr)) {
     706           2 :                 if (PIM_DEBUG_PIM_EVENTS)
     707           0 :                         zlog_debug("%s: RPF not configured for %s", __func__,
     708             :                                    up->sg_str);
     709           2 :                 return;
     710             :         }
     711             : 
     712          19 :         if (!up->rpf.source_nexthop.interface)  {
     713           8 :                 if (PIM_DEBUG_PIM_EVENTS)
     714           0 :                         zlog_debug("%s: RP not reachable for %s", __func__,
     715             :                                    up->sg_str);
     716           8 :                 return;
     717             :         }
     718             : 
     719          11 :         if (PIM_DEBUG_PIM_EVENTS) {
     720           0 :                 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
     721             :                            __func__, up->sg_str,
     722             :                            pim_upstream_state2str(up->join_state),
     723             :                            pim_upstream_state2str(new_state));
     724             :         }
     725             : 
     726          11 :         up->join_state = new_state;
     727          11 :         if (old_state != new_state)
     728          11 :                 up->state_transition = pim_time_monotonic_sec();
     729             : 
     730          11 :         pim_upstream_update_assert_tracking_desired(up);
     731             : 
     732          11 :         if (new_state == PIM_UPSTREAM_JOINED) {
     733          10 :                 pim_upstream_inherited_olist_decide(pim, up);
     734          10 :                 if (old_state != PIM_UPSTREAM_JOINED) {
     735          10 :                         int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
     736             : 
     737          10 :                         pim_msdp_up_join_state_changed(pim, up);
     738          10 :                         if (pim_upstream_could_register(up)) {
     739           4 :                                 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
     740           4 :                                 if (!old_fhr
     741           3 :                                     && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(
     742             :                                                up->flags)) {
     743           0 :                                         pim_upstream_keep_alive_timer_start(
     744             :                                                 up, pim->keep_alive_time);
     745           0 :                                         pim_register_join(up);
     746             :                                 }
     747             :                         } else {
     748           6 :                                 pim_upstream_send_join(up);
     749           6 :                                 join_timer_start(up);
     750             :                         }
     751             :                 }
     752          10 :                 if (old_state != new_state)
     753          10 :                         pim_upstream_update_use_rpt(up, true /*update_mroute*/);
     754             :         } else {
     755           1 :                 bool old_use_rpt;
     756           1 :                 bool new_use_rpt;
     757           1 :                 bool send_xg_jp = false;
     758             : 
     759           1 :                 forward_off(up);
     760             :                 /*
     761             :                  * RFC 4601 Sec 4.5.7:
     762             :                  * JoinDesired(S,G) -> False, set SPTbit to false.
     763             :                  */
     764           1 :                 if (!pim_addr_is_any(up->sg.src))
     765           1 :                         up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
     766             : 
     767           1 :                 if (old_state == PIM_UPSTREAM_JOINED)
     768           0 :                         pim_msdp_up_join_state_changed(pim, up);
     769             : 
     770           1 :                 if (old_state != new_state) {
     771           1 :                         old_use_rpt =
     772           1 :                                 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
     773           1 :                         pim_upstream_update_use_rpt(up, true /*update_mroute*/);
     774           1 :                         new_use_rpt =
     775           1 :                                 !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
     776           1 :                         if (new_use_rpt &&
     777           1 :                                         (new_use_rpt != old_use_rpt) &&
     778           1 :                                         up->parent)
     779             :                                 /* we have decided to switch from the SPT back
     780             :                                  * to the RPT which means we need to cancel
     781             :                                  * any previously sent SGrpt prunes immediately
     782             :                                  */
     783           1 :                                 send_xg_jp = true;
     784             :                 }
     785             : 
     786             :                 /* IHR, Trigger SGRpt on *,G IIF to prune S,G from RPT towards
     787             :                    RP.
     788             :                    If I am RP for G then send S,G prune to its IIF. */
     789           1 :                 if (pim_upstream_is_sg_rpt(up) && up->parent &&
     790           0 :                                 !I_am_RP(pim, up->sg.grp))
     791           1 :                         send_xg_jp = true;
     792             : 
     793           1 :                 pim_jp_agg_single_upstream_send(&up->rpf, up, 0 /* prune */);
     794             : 
     795           1 :                 if (send_xg_jp) {
     796           0 :                         if (PIM_DEBUG_PIM_TRACE_DETAIL)
     797           0 :                                 zlog_debug(
     798             :                                   "re-join RPT; *,G IIF %s S,G IIF %s ",
     799             :                                   up->parent->rpf.source_nexthop.interface ?
     800             :                                   up->parent->rpf.source_nexthop.interface->name
     801             :                                   : "Unknown",
     802             :                                   up->rpf.source_nexthop.interface ?
     803             :                                   up->rpf.source_nexthop.interface->name :
     804             :                                   "Unknown");
     805           0 :                         pim_jp_agg_single_upstream_send(&up->parent->rpf,
     806             :                                                         up->parent,
     807             :                                                         1 /* (W,G) Join */);
     808             :                 }
     809           1 :                 join_timer_stop(up);
     810             :         }
     811             : }
     812             : 
     813          85 : int pim_upstream_compare(const struct pim_upstream *up1,
     814             :                          const struct pim_upstream *up2)
     815             : {
     816          85 :         return pim_sgaddr_cmp(up1->sg, up2->sg);
     817             : }
     818             : 
     819           0 : void pim_upstream_fill_static_iif(struct pim_upstream *up,
     820             :                                 struct interface *incoming)
     821             : {
     822           0 :         up->rpf.source_nexthop.interface = incoming;
     823             : 
     824             :         /* reset other parameters to matched a connected incoming interface */
     825           0 :         up->rpf.source_nexthop.mrib_nexthop_addr = PIMADDR_ANY;
     826           0 :         up->rpf.source_nexthop.mrib_metric_preference =
     827             :                 ZEBRA_CONNECT_DISTANCE_DEFAULT;
     828           0 :         up->rpf.source_nexthop.mrib_route_metric = 0;
     829           0 :         up->rpf.rpf_addr = PIMADDR_ANY;
     830           0 : }
     831             : 
     832          15 : static struct pim_upstream *pim_upstream_new(struct pim_instance *pim,
     833             :                                              pim_sgaddr *sg,
     834             :                                              struct interface *incoming,
     835             :                                              int flags,
     836             :                                              struct pim_ifchannel *ch)
     837             : {
     838          15 :         enum pim_rpf_result rpf_result;
     839          15 :         struct pim_interface *pim_ifp;
     840          15 :         struct pim_upstream *up;
     841             : 
     842          15 :         up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
     843             : 
     844          15 :         up->pim = pim;
     845          15 :         up->sg = *sg;
     846          15 :         snprintfrr(up->sg_str, sizeof(up->sg_str), "%pSG", sg);
     847          15 :         if (ch)
     848          11 :                 ch->upstream = up;
     849             : 
     850          15 :         rb_pim_upstream_add(&pim->upstream_head, up);
     851             :         /* Set up->upstream_addr as INADDR_ANY, if RP is not
     852             :          * configured and retain the upstream data structure
     853             :          */
     854          15 :         if (!pim_rp_set_upstream_addr(pim, &up->upstream_addr, sg->src,
     855             :                                       sg->grp)) {
     856           1 :                 if (PIM_DEBUG_PIM_TRACE)
     857           0 :                         zlog_debug("%s: Received a (*,G) with no RP configured",
     858             :                                    __func__);
     859             :         }
     860             : 
     861          15 :         up->parent = pim_upstream_find_parent(pim, up);
     862          15 :         if (pim_addr_is_any(up->sg.src)) {
     863           3 :                 up->sources = list_new();
     864           3 :                 up->sources->cmp =
     865             :                         (int (*)(void *, void *))pim_upstream_compare;
     866             :         } else
     867          12 :                 up->sources = NULL;
     868             : 
     869          15 :         pim_upstream_find_new_children(pim, up);
     870          15 :         up->flags = flags;
     871          15 :         up->ref_count = 1;
     872          15 :         up->t_join_timer = NULL;
     873          15 :         up->t_ka_timer = NULL;
     874          15 :         up->t_rs_timer = NULL;
     875          15 :         up->t_msdp_reg_timer = NULL;
     876          15 :         up->join_state = PIM_UPSTREAM_NOTJOINED;
     877          15 :         up->reg_state = PIM_REG_NOINFO;
     878          15 :         up->state_transition = pim_time_monotonic_sec();
     879          15 :         up->channel_oil = pim_channel_oil_add(pim, &up->sg, __func__);
     880          15 :         up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
     881             : 
     882          15 :         up->rpf.source_nexthop.interface = NULL;
     883          15 :         up->rpf.source_nexthop.mrib_nexthop_addr = PIMADDR_ANY;
     884          15 :         up->rpf.source_nexthop.mrib_metric_preference =
     885          15 :                 router->infinite_assert_metric.metric_preference;
     886          15 :         up->rpf.source_nexthop.mrib_route_metric =
     887          15 :                 router->infinite_assert_metric.route_metric;
     888          15 :         up->rpf.rpf_addr = PIMADDR_ANY;
     889          15 :         up->ifchannels = list_new();
     890          15 :         up->ifchannels->cmp = (int (*)(void *, void *))pim_ifchannel_compare;
     891             : 
     892          15 :         if (!pim_addr_is_any(up->sg.src)) {
     893          12 :                 wheel_add_item(pim->upstream_sg_wheel, up);
     894             : 
     895             :                 /* Inherit the DF role from the parent (*, G) entry for
     896             :                  * VxLAN BUM groups
     897             :                  */
     898          12 :                 if (up->parent
     899             :                     && PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->parent->flags)
     900           2 :                     && PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->parent->flags)) {
     901           0 :                         PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(up->flags);
     902           0 :                         if (PIM_DEBUG_VXLAN)
     903           0 :                                 zlog_debug(
     904             :                                         "upstream %s inherited mlag non-df flag from parent",
     905             :                                         up->sg_str);
     906             :                 }
     907             :         }
     908             : 
     909          15 :         if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)
     910          15 :             || PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) {
     911           0 :                 pim_upstream_fill_static_iif(up, incoming);
     912           0 :                 pim_ifp = up->rpf.source_nexthop.interface->info;
     913           0 :                 assert(pim_ifp);
     914           0 :                 pim_upstream_update_use_rpt(up,
     915             :                                 false /*update_mroute*/);
     916           0 :                 pim_upstream_mroute_iif_update(up->channel_oil, __func__);
     917             : 
     918           0 :                 if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags))
     919           0 :                         pim_upstream_keep_alive_timer_start(
     920             :                                 up, pim->keep_alive_time);
     921          15 :         } else if (!pim_addr_is_any(up->upstream_addr)) {
     922          14 :                 pim_upstream_update_use_rpt(up,
     923             :                                 false /*update_mroute*/);
     924          14 :                 rpf_result = pim_rpf_update(pim, up, NULL, __func__);
     925          14 :                 if (rpf_result == PIM_RPF_FAILURE) {
     926           0 :                         if (PIM_DEBUG_PIM_TRACE)
     927           0 :                                 zlog_debug(
     928             :                                         "%s: Attempting to create upstream(%s), Unable to RPF for source",
     929             :                                         __func__, up->sg_str);
     930             :                 }
     931             : 
     932             :                 /* Consider a case where (S,G,rpt) prune is received and this
     933             :                  * upstream is getting created due to that, then as per RFC
     934             :                  * until prune pending time we need to behave same as NOINFO
     935             :                  * state, therefore do not install if OIF is NULL until then
     936             :                  * This is for PIM Conformance PIM-SM 16.3 fix
     937             :                  * When the prune pending timer pop, this mroute will get
     938             :                  * installed with none as OIF */
     939          14 :                 if (up->rpf.source_nexthop.interface &&
     940          14 :                     !(pim_upstream_empty_inherited_olist(up) && (ch != NULL) &&
     941           6 :                       PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))) {
     942          13 :                         pim_upstream_mroute_iif_update(up->channel_oil,
     943             :                                         __func__);
     944             :                 }
     945             :         }
     946             : 
     947             :         /* send the entry to the MLAG peer */
     948             :         /* XXX - duplicate send is possible here if pim_rpf_update
     949             :          * successfully resolved the nexthop
     950             :          */
     951          15 :         if (pim_up_mlag_is_local(up)
     952           7 :             || PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags))
     953           0 :                 pim_mlag_up_local_add(pim, up);
     954             : 
     955          15 :         if (PIM_DEBUG_PIM_TRACE) {
     956           0 :                 zlog_debug(
     957             :                         "%s: Created Upstream %s upstream_addr %pPAs ref count %d increment",
     958             :                         __func__, up->sg_str, &up->upstream_addr,
     959             :                         up->ref_count);
     960             :         }
     961             : 
     962          15 :         return up;
     963             : }
     964             : 
     965          50 : uint32_t pim_up_mlag_local_cost(struct pim_upstream *up)
     966             : {
     967          50 :         if (!(pim_up_mlag_is_local(up))
     968          50 :             && !(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_INTERFACE))
     969          50 :                 return router->infinite_assert_metric.route_metric;
     970             : 
     971           0 :         if ((up->rpf.source_nexthop.interface ==
     972           0 :                                 up->pim->vxlan.peerlink_rif) &&
     973           0 :                         (up->rpf.source_nexthop.mrib_route_metric <
     974           0 :                          (router->infinite_assert_metric.route_metric -
     975             :                           PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC)))
     976           0 :                 return up->rpf.source_nexthop.mrib_route_metric +
     977             :                         PIM_UPSTREAM_MLAG_PEERLINK_PLUS_METRIC;
     978             : 
     979           0 :         return up->rpf.source_nexthop.mrib_route_metric;
     980             : }
     981             : 
     982           0 : uint32_t pim_up_mlag_peer_cost(struct pim_upstream *up)
     983             : {
     984           0 :         if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_PEER))
     985           0 :                 return router->infinite_assert_metric.route_metric;
     986             : 
     987           0 :         return up->mlag.peer_mrib_metric;
     988             : }
     989             : 
     990          67 : struct pim_upstream *pim_upstream_find(struct pim_instance *pim, pim_sgaddr *sg)
     991             : {
     992          67 :         struct pim_upstream lookup;
     993          67 :         struct pim_upstream *up = NULL;
     994             : 
     995          67 :         lookup.sg = *sg;
     996          60 :         up = rb_pim_upstream_find(&pim->upstream_head, &lookup);
     997          67 :         return up;
     998             : }
     999             : 
    1000           3 : struct pim_upstream *pim_upstream_find_or_add(pim_sgaddr *sg,
    1001             :                                               struct interface *incoming,
    1002             :                                               int flags, const char *name)
    1003             : {
    1004           3 :         struct pim_interface *pim_ifp = incoming->info;
    1005             : 
    1006           3 :         return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
    1007             :                                 NULL));
    1008             : }
    1009             : 
    1010           4 : void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name)
    1011             : {
    1012             :         /* if a local MLAG reference is being created we need to send the mroute
    1013             :          * to the peer
    1014             :          */
    1015           4 :         if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags) &&
    1016           4 :                         PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags)) {
    1017           0 :                 PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(up->flags);
    1018           0 :                 pim_mlag_up_local_add(up->pim, up);
    1019             :         }
    1020             : 
    1021             :         /* when we go from non-FHR to FHR we need to re-eval traffic
    1022             :          * forwarding path
    1023             :          */
    1024           4 :         if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) &&
    1025           3 :                         PIM_UPSTREAM_FLAG_TEST_FHR(flags)) {
    1026           0 :                 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
    1027           0 :                 pim_upstream_update_use_rpt(up, true /*update_mroute*/);
    1028             :         }
    1029             : 
    1030             :         /* re-eval joinDesired; clearing peer-msdp-sa flag can
    1031             :          * cause JD to change
    1032             :          */
    1033           4 :         if (!PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags) &&
    1034           4 :                         PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(flags)) {
    1035           0 :                 PIM_UPSTREAM_FLAG_SET_SRC_MSDP(up->flags);
    1036           0 :                 pim_upstream_update_join_desired(up->pim, up);
    1037             :         }
    1038             : 
    1039           4 :         up->flags |= flags;
    1040           4 :         ++up->ref_count;
    1041           4 :         if (PIM_DEBUG_PIM_TRACE)
    1042           0 :                 zlog_debug("%s(%s): upstream %s ref count %d increment",
    1043             :                            __func__, name, up->sg_str, up->ref_count);
    1044           4 : }
    1045             : 
    1046          15 : struct pim_upstream *pim_upstream_add(struct pim_instance *pim, pim_sgaddr *sg,
    1047             :                                       struct interface *incoming, int flags,
    1048             :                                       const char *name,
    1049             :                                       struct pim_ifchannel *ch)
    1050             : {
    1051          15 :         struct pim_upstream *up = NULL;
    1052          15 :         int found = 0;
    1053             : 
    1054          15 :         up = pim_upstream_find(pim, sg);
    1055          15 :         if (up) {
    1056           0 :                 pim_upstream_ref(up, flags, name);
    1057           0 :                 found = 1;
    1058             :         } else {
    1059          15 :                 up = pim_upstream_new(pim, sg, incoming, flags, ch);
    1060             :         }
    1061             : 
    1062          15 :         if (PIM_DEBUG_PIM_TRACE) {
    1063           0 :                 zlog_debug(
    1064             :                         "%s(%s): %s, iif %pPA (%s) found: %d: ref_count: %d",
    1065             :                         __func__, name, up->sg_str, &up->rpf.rpf_addr,
    1066             :                         up->rpf.source_nexthop.interface ? up->rpf.source_nexthop
    1067             :                                                                    .interface->name
    1068             :                                                          : "Unknown",
    1069             :                         found, up->ref_count);
    1070             :         }
    1071             : 
    1072          15 :         return up;
    1073             : }
    1074             : 
    1075             : /*
    1076             :  * Passed in up must be the upstream for ch.  starch is NULL if no
    1077             :  * information
    1078             :  * This function is copied over from
    1079             :  * pim_upstream_evaluate_join_desired_interface but limited to
    1080             :  * parent (*,G)'s includes/joins.
    1081             :  */
    1082           0 : int pim_upstream_eval_inherit_if(struct pim_upstream *up,
    1083             :                                                  struct pim_ifchannel *ch,
    1084             :                                                  struct pim_ifchannel *starch)
    1085             : {
    1086             :         /* if there is an explicit prune for this interface we cannot
    1087             :          * add it to the OIL
    1088             :          */
    1089           0 :         if (ch) {
    1090           0 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
    1091             :                         return 0;
    1092             :         }
    1093             : 
    1094             :         /* Check if the OIF can be inherited fron the (*,G) entry
    1095             :          */
    1096           0 :         if (starch) {
    1097           0 :                 if (!pim_macro_ch_lost_assert(starch)
    1098           0 :                     && pim_macro_chisin_joins_or_include(starch))
    1099             :                         return 1;
    1100             :         }
    1101             : 
    1102             :         return 0;
    1103             : }
    1104             : 
    1105             : /*
    1106             :  * Passed in up must be the upstream for ch.  starch is NULL if no
    1107             :  * information
    1108             :  */
    1109          58 : int pim_upstream_evaluate_join_desired_interface(struct pim_upstream *up,
    1110             :                                                  struct pim_ifchannel *ch,
    1111             :                                                  struct pim_ifchannel *starch)
    1112             : {
    1113          58 :         if (ch) {
    1114          55 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
    1115             :                         return 0;
    1116             : 
    1117          51 :                 if (!pim_macro_ch_lost_assert(ch)
    1118          51 :                     && pim_macro_chisin_joins_or_include(ch))
    1119             :                         return 1;
    1120             :         }
    1121             : 
    1122             :         /*
    1123             :          * joins (*,G)
    1124             :          */
    1125          17 :         if (starch) {
    1126             :                 /* XXX: check on this with donald
    1127             :                  * we are looking for PIM_IF_FLAG_MASK_S_G_RPT in
    1128             :                  * upstream flags?
    1129             :                  */
    1130             : #if 0
    1131             :                 if (PIM_IF_FLAG_TEST_S_G_RPT(starch->upstream->flags))
    1132             :                         return 0;
    1133             : #endif
    1134             : 
    1135           3 :                 if (!pim_macro_ch_lost_assert(starch)
    1136           3 :                     && pim_macro_chisin_joins_or_include(starch))
    1137             :                         return 1;
    1138             :         }
    1139             : 
    1140             :         return 0;
    1141             : }
    1142             : 
    1143             : /* Returns true if immediate OIL is empty and is used to evaluate
    1144             :  * JoinDesired. See pim_upstream_evaluate_join_desired.
    1145             :  */
    1146          64 : static bool pim_upstream_empty_immediate_olist(struct pim_instance *pim,
    1147             :                                        struct pim_upstream *up)
    1148             : {
    1149          64 :         struct interface *ifp;
    1150          64 :         struct pim_ifchannel *ch;
    1151             : 
    1152         356 :         FOR_ALL_INTERFACES (pim->vrf, ifp) {
    1153         251 :                 if (!ifp->info)
    1154          28 :                         continue;
    1155             : 
    1156         223 :                 ch = pim_ifchannel_find(ifp, &up->sg);
    1157         223 :                 if (!ch)
    1158         185 :                         continue;
    1159             : 
    1160             :                 /* If we have even one immediate OIF we can return with
    1161             :                  * not-empty
    1162             :                  */
    1163          38 :                 if (pim_upstream_evaluate_join_desired_interface(up, ch,
    1164             :                                             NULL /* starch */))
    1165             :                         return false;
    1166             :         } /* scan iface channel list */
    1167             : 
    1168             :         /* immediate_oil is empty */
    1169             :         return true;
    1170             : }
    1171             : 
    1172             : 
    1173           3 : static inline bool pim_upstream_is_msdp_peer_sa(struct pim_upstream *up)
    1174             : {
    1175           3 :         return PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags);
    1176             : }
    1177             : 
    1178             : /*
    1179             :  *   bool JoinDesired(*,G) {
    1180             :  *       if (immediate_olist(*,G) != NULL)
    1181             :  *           return TRUE
    1182             :  *       else
    1183             :  *           return FALSE
    1184             :  *   }
    1185             :  *
    1186             :  *   bool JoinDesired(S,G) {
    1187             :  *       return( immediate_olist(S,G) != NULL
    1188             :  *           OR ( KeepaliveTimer(S,G) is running
    1189             :  *           AND inherited_olist(S,G) != NULL ) )
    1190             :  *   }
    1191             :  */
    1192          64 : bool pim_upstream_evaluate_join_desired(struct pim_instance *pim,
    1193             :                                        struct pim_upstream *up)
    1194             : {
    1195          64 :         bool empty_imm_oil;
    1196          64 :         bool empty_inh_oil;
    1197             : 
    1198          64 :         empty_imm_oil = pim_upstream_empty_immediate_olist(pim, up);
    1199             : 
    1200             :         /* (*,G) */
    1201          64 :         if (pim_addr_is_any(up->sg.src))
    1202           9 :                 return !empty_imm_oil;
    1203             : 
    1204             :         /* (S,G) */
    1205          55 :         if (!empty_imm_oil)
    1206             :                 return true;
    1207          72 :         empty_inh_oil = pim_upstream_empty_inherited_olist(up);
    1208          36 :         if (!empty_inh_oil &&
    1209           6 :                         (pim_upstream_is_kat_running(up) ||
    1210           3 :                          pim_upstream_is_msdp_peer_sa(up)))
    1211             :                 return true;
    1212             : 
    1213             :         return false;
    1214             : }
    1215             : 
    1216             : /*
    1217             :   See also pim_upstream_evaluate_join_desired() above.
    1218             : */
    1219          60 : void pim_upstream_update_join_desired(struct pim_instance *pim,
    1220             :                                       struct pim_upstream *up)
    1221             : {
    1222          60 :         int was_join_desired; /* boolean */
    1223          60 :         int is_join_desired;  /* boolean */
    1224             : 
    1225          60 :         was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
    1226             : 
    1227          60 :         is_join_desired = pim_upstream_evaluate_join_desired(pim, up);
    1228          60 :         if (is_join_desired)
    1229          22 :                 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
    1230             :         else
    1231          38 :                 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
    1232             : 
    1233             :         /* switched from false to true */
    1234          60 :         if (is_join_desired && (up->join_state == PIM_UPSTREAM_NOTJOINED)) {
    1235          11 :                 pim_upstream_switch(pim, up, PIM_UPSTREAM_JOINED);
    1236          11 :                 return;
    1237             :         }
    1238             : 
    1239             :         /* switched from true to false */
    1240          49 :         if (!is_join_desired && was_join_desired) {
    1241          10 :                 pim_upstream_switch(pim, up, PIM_UPSTREAM_NOTJOINED);
    1242          10 :                 return;
    1243             :         }
    1244             : }
    1245             : 
    1246             : /*
    1247             :   RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
    1248             :   Transitions from Joined State
    1249             :   RPF'(S,G) GenID changes
    1250             : 
    1251             :   The upstream (S,G) state machine remains in Joined state.  If the
    1252             :   Join Timer is set to expire in more than t_override seconds, reset
    1253             :   it so that it expires after t_override seconds.
    1254             : */
    1255           1 : void pim_upstream_rpf_genid_changed(struct pim_instance *pim,
    1256             :                                     pim_addr neigh_addr)
    1257             : {
    1258           1 :         struct pim_upstream *up;
    1259             : 
    1260             :         /*
    1261             :          * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
    1262             :          */
    1263           2 :         frr_each (rb_pim_upstream, &pim->upstream_head, up) {
    1264           0 :                 pim_addr rpf_addr;
    1265             : 
    1266           0 :                 rpf_addr = up->rpf.rpf_addr;
    1267             : 
    1268           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1269           0 :                         zlog_debug(
    1270             :                                 "%s: matching neigh=%pPA against upstream (S,G)=%s[%s] joined=%d rpf_addr=%pPA",
    1271             :                                 __func__, &neigh_addr, up->sg_str,
    1272             :                                 pim->vrf->name,
    1273             :                                 up->join_state == PIM_UPSTREAM_JOINED,
    1274             :                                 &rpf_addr);
    1275             : 
    1276             :                 /* consider only (S,G) upstream in Joined state */
    1277           0 :                 if (up->join_state != PIM_UPSTREAM_JOINED)
    1278           0 :                         continue;
    1279             : 
    1280             :                 /* match RPF'(S,G)=neigh_addr */
    1281           0 :                 if (pim_addr_cmp(rpf_addr, neigh_addr))
    1282           0 :                         continue;
    1283             : 
    1284           0 :                 pim_upstream_join_timer_decrease_to_t_override(
    1285             :                         "RPF'(S,G) GenID change", up);
    1286             :         }
    1287           1 : }
    1288             : 
    1289             : 
    1290          14 : void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
    1291             :                                         struct interface *old_rpf_ifp)
    1292             : {
    1293          14 :         struct listnode *chnode;
    1294          14 :         struct listnode *chnextnode;
    1295          14 :         struct pim_ifchannel *ch;
    1296             : 
    1297             :         /* search all ifchannels */
    1298          28 :         for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
    1299           0 :                 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
    1300           0 :                         if (
    1301             :                                 /* RPF_interface(S) was NOT I */
    1302           0 :                                 (old_rpf_ifp == ch->interface) &&
    1303             :                                 /* RPF_interface(S) stopped being I */
    1304           0 :                                 (ch->upstream->rpf.source_nexthop
    1305           0 :                                         .interface) &&
    1306             :                                 (ch->upstream->rpf.source_nexthop
    1307             :                                         .interface != ch->interface)) {
    1308           0 :                                 assert_action_a5(ch);
    1309             :                         }
    1310             :                 } /* PIM_IFASSERT_I_AM_LOSER */
    1311             : 
    1312           0 :                 pim_ifchannel_update_assert_tracking_desired(ch);
    1313             :         }
    1314          14 : }
    1315             : 
    1316          18 : void pim_upstream_update_could_assert(struct pim_upstream *up)
    1317             : {
    1318          18 :         struct listnode *chnode;
    1319          18 :         struct listnode *chnextnode;
    1320          18 :         struct pim_ifchannel *ch;
    1321             : 
    1322             :         /* scan per-interface (S,G) state */
    1323          40 :         for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
    1324           4 :                 pim_ifchannel_update_could_assert(ch);
    1325             :         } /* scan iface channel list */
    1326          18 : }
    1327             : 
    1328          14 : void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
    1329             : {
    1330          14 :         struct listnode *chnode;
    1331          14 :         struct listnode *chnextnode;
    1332          14 :         struct pim_ifchannel *ch;
    1333             : 
    1334             :         /* scan per-interface (S,G) state */
    1335          28 :         for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
    1336           0 :                 pim_ifchannel_update_my_assert_metric(ch);
    1337             : 
    1338             :         } /* scan iface channel list */
    1339          14 : }
    1340             : 
    1341          11 : static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
    1342             : {
    1343          11 :         struct listnode *chnode;
    1344          11 :         struct listnode *chnextnode;
    1345          11 :         struct pim_interface *pim_ifp;
    1346          11 :         struct pim_ifchannel *ch;
    1347             : 
    1348             :         /* scan per-interface (S,G) state */
    1349          32 :         for (ALL_LIST_ELEMENTS(up->ifchannels, chnode, chnextnode, ch)) {
    1350          10 :                 if (!ch->interface)
    1351           0 :                         continue;
    1352          10 :                 pim_ifp = ch->interface->info;
    1353          10 :                 if (!pim_ifp)
    1354           0 :                         continue;
    1355             : 
    1356          10 :                 pim_ifchannel_update_assert_tracking_desired(ch);
    1357             : 
    1358             :         } /* scan iface channel list */
    1359          11 : }
    1360             : 
    1361             : /* When kat is stopped CouldRegister goes to false so we need to
    1362             :  * transition  the (S, G) on FHR to NI state and remove reg tunnel
    1363             :  * from the OIL */
    1364           0 : static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim,
    1365             :                                         struct pim_upstream *up)
    1366             : {
    1367           0 :         if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
    1368             :                 return;
    1369             : 
    1370           0 :         if (PIM_DEBUG_PIM_TRACE)
    1371           0 :                 zlog_debug("kat expired on %s; clear fhr reg state",
    1372             :                            up->sg_str);
    1373             : 
    1374             :         /* stop reg-stop timer */
    1375           0 :         THREAD_OFF(up->t_rs_timer);
    1376             :         /* remove regiface from the OIL if it is there*/
    1377           0 :         pim_channel_del_oif(up->channel_oil, pim->regiface,
    1378             :                             PIM_OIF_FLAG_PROTO_PIM, __func__);
    1379             :         /* clear the register state */
    1380           0 :         up->reg_state = PIM_REG_NOINFO;
    1381           0 :         PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
    1382             : }
    1383             : 
    1384             : /* When kat is started CouldRegister can go to true. And if it does we
    1385             :  * need to transition  the (S, G) on FHR to JOINED state and add reg tunnel
    1386             :  * to the OIL */
    1387           4 : static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
    1388             : {
    1389           4 :         if (pim_upstream_could_register(up)) {
    1390           1 :                 if (PIM_DEBUG_PIM_TRACE)
    1391           0 :                         zlog_debug(
    1392             :                                 "kat started on %s; set fhr reg state to joined",
    1393             :                                 up->sg_str);
    1394             : 
    1395           1 :                 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
    1396           1 :                 if (up->reg_state == PIM_REG_NOINFO)
    1397           1 :                         pim_register_join(up);
    1398           1 :                 pim_upstream_update_use_rpt(up, true /*update_mroute*/);
    1399             :         }
    1400           4 : }
    1401             : 
    1402             : /*
    1403             :  * On an RP, the PMBR value must be cleared when the
    1404             :  * Keepalive Timer expires
    1405             :  * KAT expiry indicates that flow is inactive. If the flow was created or
    1406             :  * maintained by activity now is the time to deref it.
    1407             :  */
    1408           0 : struct pim_upstream *pim_upstream_keep_alive_timer_proc(
    1409             :                 struct pim_upstream *up)
    1410             : {
    1411           0 :         struct pim_instance *pim;
    1412             : 
    1413           0 :         pim = up->channel_oil->pim;
    1414             : 
    1415           0 :         if (PIM_UPSTREAM_FLAG_TEST_DISABLE_KAT_EXPIRY(up->flags)) {
    1416             :                 /* if the router is a PIM vxlan encapsulator we prevent expiry
    1417             :                  * of KAT as the mroute is pre-setup without any traffic
    1418             :                  */
    1419           0 :                 pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time);
    1420           0 :                 return up;
    1421             :         }
    1422             : 
    1423           0 :         if (I_am_RP(pim, up->sg.grp)) {
    1424             :                 /*
    1425             :                  * Handle Border Router
    1426             :                  * We need to do more here :)
    1427             :                  * But this is the start.
    1428             :                  */
    1429           0 :         }
    1430             : 
    1431             :         /* source is no longer active - pull the SA from MSDP's cache */
    1432           0 :         pim_msdp_sa_local_del(pim, &up->sg);
    1433             : 
    1434             :         /* JoinDesired can change when KAT is started or stopped */
    1435           0 :         pim_upstream_update_join_desired(pim, up);
    1436             : 
    1437             :         /* if entry was created because of activity we need to deref it */
    1438           0 :         if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
    1439           0 :                 pim_upstream_fhr_kat_expiry(pim, up);
    1440           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1441           0 :                         zlog_debug(
    1442             :                                 "kat expired on %s[%s]; remove stream reference",
    1443             :                                 up->sg_str, pim->vrf->name);
    1444           0 :                 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
    1445             : 
    1446             :                 /* Return if upstream entry got deleted.*/
    1447           0 :                 if (!pim_upstream_del(pim, up, __func__))
    1448             :                         return NULL;
    1449             :         }
    1450           0 :         if (PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(up->flags)) {
    1451           0 :                 PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(up->flags);
    1452             : 
    1453           0 :                 if (!pim_upstream_del(pim, up, __func__))
    1454             :                         return NULL;
    1455             :         }
    1456             : 
    1457             :         /* upstream reference would have been added to track the local
    1458             :          * membership if it is LHR. We have to clear it when KAT expires.
    1459             :          * Otherwise would result in stale entry with uncleared ref count.
    1460             :          */
    1461           0 :         if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) {
    1462           0 :                 struct pim_upstream *parent = up->parent;
    1463             : 
    1464           0 :                 PIM_UPSTREAM_FLAG_UNSET_SRC_LHR(up->flags);
    1465           0 :                 up = pim_upstream_del(pim, up, __func__);
    1466             : 
    1467           0 :                 if (parent) {
    1468           0 :                         pim_jp_agg_single_upstream_send(&parent->rpf, parent,
    1469             :                                                         true);
    1470             :                 }
    1471             :         }
    1472             : 
    1473             :         return up;
    1474             : }
    1475           0 : static void pim_upstream_keep_alive_timer(struct thread *t)
    1476             : {
    1477           0 :         struct pim_upstream *up;
    1478             : 
    1479           0 :         up = THREAD_ARG(t);
    1480             : 
    1481             :         /* pull the stats and re-check */
    1482           0 :         if (pim_upstream_sg_running_proc(up))
    1483             :                 /* kat was restarted because of new activity */
    1484             :                 return;
    1485             : 
    1486           0 :         pim_upstream_keep_alive_timer_proc(up);
    1487             : }
    1488             : 
    1489          11 : void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time)
    1490             : {
    1491          11 :         if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
    1492           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1493           0 :                         zlog_debug("kat start on %s with no stream reference",
    1494             :                                    up->sg_str);
    1495             :         }
    1496          11 :         THREAD_OFF(up->t_ka_timer);
    1497          11 :         thread_add_timer(router->master, pim_upstream_keep_alive_timer, up,
    1498             :                          time, &up->t_ka_timer);
    1499             : 
    1500             :         /* any time keepalive is started against a SG we will have to
    1501             :          * re-evaluate our active source database */
    1502          11 :         pim_msdp_sa_local_update(up);
    1503             :         /* JoinDesired can change when KAT is started or stopped */
    1504          11 :         pim_upstream_update_join_desired(up->pim, up);
    1505          11 : }
    1506             : 
    1507             : /* MSDP on RP needs to know if a source is registerable to this RP */
    1508           0 : static void pim_upstream_msdp_reg_timer(struct thread *t)
    1509             : {
    1510           0 :         struct pim_upstream *up = THREAD_ARG(t);
    1511           0 :         struct pim_instance *pim = up->channel_oil->pim;
    1512             : 
    1513             :         /* source is no longer active - pull the SA from MSDP's cache */
    1514           0 :         pim_msdp_sa_local_del(pim, &up->sg);
    1515           0 : }
    1516             : 
    1517           1 : void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
    1518             : {
    1519           1 :         THREAD_OFF(up->t_msdp_reg_timer);
    1520           1 :         thread_add_timer(router->master, pim_upstream_msdp_reg_timer, up,
    1521             :                          PIM_MSDP_REG_RXED_PERIOD, &up->t_msdp_reg_timer);
    1522             : 
    1523           1 :         pim_msdp_sa_local_update(up);
    1524           1 : }
    1525             : 
    1526             : /*
    1527             :  * 4.2.1 Last-Hop Switchover to the SPT
    1528             :  *
    1529             :  *  In Sparse-Mode PIM, last-hop routers join the shared tree towards the
    1530             :  *  RP.  Once traffic from sources to joined groups arrives at a last-hop
    1531             :  *  router, it has the option of switching to receive the traffic on a
    1532             :  *  shortest path tree (SPT).
    1533             :  *
    1534             :  *  The decision for a router to switch to the SPT is controlled as
    1535             :  *  follows:
    1536             :  *
    1537             :  *    void
    1538             :  *    CheckSwitchToSpt(S,G) {
    1539             :  *      if ( ( pim_include(*,G) (-) pim_exclude(S,G)
    1540             :  *             (+) pim_include(S,G) != NULL )
    1541             :  *           AND SwitchToSptDesired(S,G) ) {
    1542             :  *             # Note: Restarting the KAT will result in the SPT switch
    1543             :  *             set KeepaliveTimer(S,G) to Keepalive_Period
    1544             :  *      }
    1545             :  *    }
    1546             :  *
    1547             :  *  SwitchToSptDesired(S,G) is a policy function that is implementation
    1548             :  *  defined.  An "infinite threshold" policy can be implemented by making
    1549             :  *  SwitchToSptDesired(S,G) return false all the time.  A "switch on
    1550             :  *  first packet" policy can be implemented by making
    1551             :  *  SwitchToSptDesired(S,G) return true once a single packet has been
    1552             :  *  received for the source and group.
    1553             :  */
    1554           2 : int pim_upstream_switch_to_spt_desired_on_rp(struct pim_instance *pim,
    1555             :                                              pim_sgaddr *sg)
    1556             : {
    1557           2 :         if (I_am_RP(pim, sg->grp))
    1558           2 :                 return 1;
    1559             : 
    1560             :         return 0;
    1561             : }
    1562             : 
    1563          14 : int pim_upstream_is_sg_rpt(struct pim_upstream *up)
    1564             : {
    1565          14 :         struct listnode *chnode;
    1566          14 :         struct pim_ifchannel *ch;
    1567             : 
    1568          42 :         for (ALL_LIST_ELEMENTS_RO(up->ifchannels, chnode, ch)) {
    1569          14 :                 if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
    1570             :                         return 1;
    1571             :         }
    1572             : 
    1573             :         return 0;
    1574             : }
    1575             : /*
    1576             :  *  After receiving a packet set SPTbit:
    1577             :  *   void
    1578             :  *   Update_SPTbit(S,G,iif) {
    1579             :  *     if ( iif == RPF_interface(S)
    1580             :  *           AND JoinDesired(S,G) == true
    1581             :  *           AND ( DirectlyConnected(S) == true
    1582             :  *                 OR RPF_interface(S) != RPF_interface(RP(G))
    1583             :  *                 OR inherited_olist(S,G,rpt) == NULL
    1584             :  *                 OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
    1585             :  *                      ( RPF'(S,G) != NULL ) )
    1586             :  *                 OR ( I_Am_Assert_Loser(S,G,iif) ) {
    1587             :  *        Set SPTbit(S,G) to true
    1588             :  *     }
    1589             :  *   }
    1590             :  */
    1591           4 : void pim_upstream_set_sptbit(struct pim_upstream *up,
    1592             :                              struct interface *incoming)
    1593             : {
    1594           4 :         struct pim_upstream *starup = up->parent;
    1595             : 
    1596             :         // iif == RPF_interfvace(S)
    1597           4 :         if (up->rpf.source_nexthop.interface != incoming) {
    1598           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1599           0 :                         zlog_debug(
    1600             :                                 "%s: Incoming Interface: %s is different than RPF_interface(S) %s",
    1601             :                                 __func__, incoming->name,
    1602             :                                 up->rpf.source_nexthop.interface->name);
    1603           0 :                 return;
    1604             :         }
    1605             : 
    1606             :         // AND JoinDesired(S,G) == true
    1607           4 :         if (!pim_upstream_evaluate_join_desired(up->channel_oil->pim, up)) {
    1608           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1609           0 :                         zlog_debug("%s: %s Join is not Desired", __func__,
    1610             :                                    up->sg_str);
    1611           0 :                 return;
    1612             :         }
    1613             : 
    1614             :         // DirectlyConnected(S) == true
    1615           4 :         if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
    1616             :                                        up->sg.src)) {
    1617           1 :                 if (PIM_DEBUG_PIM_TRACE)
    1618           0 :                         zlog_debug("%s: %s is directly connected to the source",
    1619             :                                    __func__, up->sg_str);
    1620           1 :                 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
    1621           1 :                 return;
    1622             :         }
    1623             : 
    1624             :         // OR RPF_interface(S) != RPF_interface(RP(G))
    1625           3 :         if (!starup
    1626           0 :             || up->rpf.source_nexthop
    1627           0 :                                .interface != starup->rpf.source_nexthop.interface) {
    1628           3 :                 struct pim_upstream *starup = up->parent;
    1629             : 
    1630           3 :                 if (PIM_DEBUG_PIM_TRACE)
    1631           0 :                         zlog_debug(
    1632             :                                 "%s: %s RPF_interface(S) != RPF_interface(RP(G))",
    1633             :                                 __func__, up->sg_str);
    1634           3 :                 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
    1635             : 
    1636           3 :                 pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
    1637           3 :                 return;
    1638             :         }
    1639             : 
    1640             :         // OR inherited_olist(S,G,rpt) == NULL
    1641           0 :         if (pim_upstream_is_sg_rpt(up)
    1642           0 :             && pim_upstream_empty_inherited_olist(up)) {
    1643           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1644           0 :                         zlog_debug("%s: %s OR inherited_olist(S,G,rpt) == NULL",
    1645             :                                    __func__, up->sg_str);
    1646           0 :                 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
    1647           0 :                 return;
    1648             :         }
    1649             : 
    1650             :         // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
    1651             :         //      ( RPF'(S,G) != NULL ) )
    1652           0 :         if (up->parent && pim_rpf_is_same(&up->rpf, &up->parent->rpf)) {
    1653           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1654           0 :                         zlog_debug("%s: %s RPF'(S,G) is the same as RPF'(*,G)",
    1655             :                                    __func__, up->sg_str);
    1656           0 :                 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
    1657           0 :                 return;
    1658             :         }
    1659             : 
    1660             :         return;
    1661             : }
    1662             : 
    1663           8 : const char *pim_upstream_state2str(enum pim_upstream_state join_state)
    1664             : {
    1665           8 :         switch (join_state) {
    1666             :         case PIM_UPSTREAM_NOTJOINED:
    1667             :                 return "NotJoined";
    1668           1 :         case PIM_UPSTREAM_JOINED:
    1669           1 :                 return "Joined";
    1670             :         }
    1671           0 :         return "Unknown";
    1672             : }
    1673             : 
    1674           8 : const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str,
    1675             :                               size_t state_str_len)
    1676             : {
    1677           8 :         switch (reg_state) {
    1678           2 :         case PIM_REG_NOINFO:
    1679           2 :                 strlcpy(state_str, "RegNoInfo", state_str_len);
    1680           2 :                 break;
    1681           0 :         case PIM_REG_JOIN:
    1682           0 :                 strlcpy(state_str, "RegJoined", state_str_len);
    1683           0 :                 break;
    1684           0 :         case PIM_REG_JOIN_PENDING:
    1685           0 :                 strlcpy(state_str, "RegJoinPend", state_str_len);
    1686           0 :                 break;
    1687           6 :         case PIM_REG_PRUNE:
    1688           6 :                 strlcpy(state_str, "RegPrune", state_str_len);
    1689           6 :                 break;
    1690             :         }
    1691           8 :         return state_str;
    1692             : }
    1693             : 
    1694           0 : static void pim_upstream_register_stop_timer(struct thread *t)
    1695             : {
    1696           0 :         struct pim_interface *pim_ifp;
    1697           0 :         struct pim_instance *pim;
    1698           0 :         struct pim_upstream *up;
    1699           0 :         up = THREAD_ARG(t);
    1700           0 :         pim = up->channel_oil->pim;
    1701             : 
    1702           0 :         if (PIM_DEBUG_PIM_TRACE) {
    1703           0 :                 char state_str[PIM_REG_STATE_STR_LEN];
    1704           0 :                 zlog_debug("%s: (S,G)=%s[%s] upstream register stop timer %s",
    1705             :                            __func__, up->sg_str, pim->vrf->name,
    1706             :                            pim_reg_state2str(up->reg_state, state_str,
    1707             :                                              sizeof(state_str)));
    1708             :         }
    1709             : 
    1710           0 :         switch (up->reg_state) {
    1711           0 :         case PIM_REG_JOIN_PENDING:
    1712           0 :                 up->reg_state = PIM_REG_JOIN;
    1713           0 :                 pim_channel_add_oif(up->channel_oil, pim->regiface,
    1714             :                                     PIM_OIF_FLAG_PROTO_PIM,
    1715             :                                         __func__);
    1716           0 :                 pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
    1717           0 :                 break;
    1718             :         case PIM_REG_JOIN:
    1719             :                 break;
    1720           0 :         case PIM_REG_PRUNE:
    1721             :                 /* This is equalent to Couldreg -> False */
    1722           0 :                 if (!up->rpf.source_nexthop.interface) {
    1723           0 :                         if (PIM_DEBUG_PIM_TRACE)
    1724           0 :                                 zlog_debug("%s: up %s RPF is not present",
    1725             :                                            __func__, up->sg_str);
    1726           0 :                         up->reg_state = PIM_REG_NOINFO;
    1727           0 :                         return;
    1728             :                 }
    1729             : 
    1730           0 :                 pim_ifp = up->rpf.source_nexthop.interface->info;
    1731           0 :                 if (!pim_ifp) {
    1732           0 :                         if (PIM_DEBUG_PIM_TRACE)
    1733           0 :                                 zlog_debug(
    1734             :                                         "%s: Interface: %s is not configured for pim",
    1735             :                                         __func__,
    1736             :                                         up->rpf.source_nexthop.interface->name);
    1737           0 :                         return;
    1738             :                 }
    1739           0 :                 up->reg_state = PIM_REG_JOIN_PENDING;
    1740           0 :                 pim_upstream_start_register_stop_timer(up, 1);
    1741             : 
    1742           0 :                 if (((up->channel_oil->cc.lastused / 100)
    1743           0 :                      > pim->keep_alive_time)
    1744           0 :                     && (I_am_RP(pim_ifp->pim, up->sg.grp))) {
    1745           0 :                         if (PIM_DEBUG_PIM_TRACE)
    1746           0 :                                 zlog_debug(
    1747             :                                         "%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while",
    1748             :                                         __func__);
    1749           0 :                         return;
    1750             :                 }
    1751           0 :                 pim_null_register_send(up);
    1752           0 :                 break;
    1753             :         case PIM_REG_NOINFO:
    1754             :                 break;
    1755             :         }
    1756             : }
    1757             : 
    1758           3 : void pim_upstream_start_register_stop_timer(struct pim_upstream *up,
    1759             :                                             int null_register)
    1760             : {
    1761           3 :         uint32_t time;
    1762             : 
    1763           3 :         THREAD_OFF(up->t_rs_timer);
    1764             : 
    1765           3 :         if (!null_register) {
    1766           3 :                 uint32_t lower = (0.5 * router->register_suppress_time);
    1767           3 :                 uint32_t upper = (1.5 * router->register_suppress_time);
    1768           3 :                 time = lower + (frr_weak_random() % (upper - lower + 1));
    1769             :                 /* Make sure we don't wrap around */
    1770           3 :                 if (time >= router->register_probe_time)
    1771           3 :                         time -= router->register_probe_time;
    1772             :                 else
    1773             :                         time = 0;
    1774             :         } else
    1775           0 :                 time = router->register_probe_time;
    1776             : 
    1777           3 :         if (PIM_DEBUG_PIM_TRACE) {
    1778           0 :                 zlog_debug(
    1779             :                         "%s: (S,G)=%s Starting upstream register stop timer %d",
    1780             :                         __func__, up->sg_str, time);
    1781             :         }
    1782           3 :         thread_add_timer(router->master, pim_upstream_register_stop_timer, up,
    1783             :                          time, &up->t_rs_timer);
    1784           3 : }
    1785             : 
    1786          22 : int pim_upstream_inherited_olist_decide(struct pim_instance *pim,
    1787             :                                         struct pim_upstream *up)
    1788             : {
    1789          22 :         struct interface *ifp;
    1790          22 :         struct pim_ifchannel *ch, *starch;
    1791          22 :         struct pim_upstream *starup = up->parent;
    1792          22 :         int output_intf = 0;
    1793             : 
    1794          22 :         if (!up->rpf.source_nexthop.interface)
    1795           0 :                 if (PIM_DEBUG_PIM_TRACE)
    1796           0 :                         zlog_debug("%s: up %s RPF is not present", __func__,
    1797             :                                    up->sg_str);
    1798             : 
    1799         138 :         FOR_ALL_INTERFACES (pim->vrf, ifp) {
    1800          94 :                 struct pim_interface *pim_ifp;
    1801          94 :                 if (!ifp->info)
    1802           8 :                         continue;
    1803             : 
    1804          86 :                 ch = pim_ifchannel_find(ifp, &up->sg);
    1805             : 
    1806          86 :                 if (starup)
    1807          15 :                         starch = pim_ifchannel_find(ifp, &starup->sg);
    1808             :                 else
    1809             :                         starch = NULL;
    1810             : 
    1811          86 :                 if (!ch && !starch)
    1812          69 :                         continue;
    1813             : 
    1814          17 :                 pim_ifp = ifp->info;
    1815          17 :                 if (PIM_I_am_DualActive(pim_ifp)
    1816           0 :                     && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)
    1817           0 :                     && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->flags)
    1818           0 :                         || !PIM_UPSTREAM_FLAG_TEST_MLAG_PEER(up->flags)))
    1819           0 :                         continue;
    1820          17 :                 if (pim_upstream_evaluate_join_desired_interface(up, ch,
    1821             :                                                                  starch)) {
    1822          17 :                         int flag = 0;
    1823             : 
    1824          17 :                         if (!ch)
    1825             :                                 flag = PIM_OIF_FLAG_PROTO_STAR;
    1826             :                         else {
    1827          14 :                                 if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags))
    1828           4 :                                         flag = PIM_OIF_FLAG_PROTO_GM;
    1829          14 :                                 if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags))
    1830          10 :                                         flag |= PIM_OIF_FLAG_PROTO_PIM;
    1831          14 :                                 if (starch)
    1832           0 :                                         flag |= PIM_OIF_FLAG_PROTO_STAR;
    1833             :                         }
    1834             : 
    1835          17 :                         pim_channel_add_oif(up->channel_oil, ifp, flag,
    1836             :                                         __func__);
    1837          17 :                         output_intf++;
    1838             :                 }
    1839             :         }
    1840             : 
    1841          22 :         return output_intf;
    1842             : }
    1843             : 
    1844             : /*
    1845             :  * For a given upstream, determine the inherited_olist
    1846             :  * and apply it.
    1847             :  *
    1848             :  * inherited_olist(S,G,rpt) =
    1849             :  *           ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
    1850             :  *      (+) ( pim_include(*,G) (-) pim_exclude(S,G))
    1851             :  *      (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
    1852             :  *
    1853             :  *  inherited_olist(S,G) =
    1854             :  *      inherited_olist(S,G,rpt) (+)
    1855             :  *      joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
    1856             :  *
    1857             :  * return 1 if there are any output interfaces
    1858             :  * return 0 if there are not any output interfaces
    1859             :  */
    1860           9 : int pim_upstream_inherited_olist(struct pim_instance *pim,
    1861             :                                  struct pim_upstream *up)
    1862             : {
    1863           9 :         int output_intf = pim_upstream_inherited_olist_decide(pim, up);
    1864             : 
    1865             :         /*
    1866             :          * If we have output_intf switch state to Join and work like normal
    1867             :          * If we don't have an output_intf that means we are probably a
    1868             :          * switch on a stick so turn on forwarding to just accept the
    1869             :          * incoming packets so we don't bother the other stuff!
    1870             :          */
    1871           9 :         pim_upstream_update_join_desired(pim, up);
    1872             : 
    1873           9 :         if (!output_intf)
    1874           3 :                 forward_on(up);
    1875             : 
    1876           9 :         return output_intf;
    1877             : }
    1878             : 
    1879          54 : int pim_upstream_empty_inherited_olist(struct pim_upstream *up)
    1880             : {
    1881          54 :         return pim_channel_oil_empty(up->channel_oil);
    1882             : }
    1883             : 
    1884             : /*
    1885             :  * When we have a new neighbor,
    1886             :  * find upstreams that don't have their rpf_addr
    1887             :  * set and see if the new neighbor allows
    1888             :  * the join to be sent
    1889             :  */
    1890          38 : void pim_upstream_find_new_rpf(struct pim_instance *pim)
    1891             : {
    1892          38 :         struct pim_upstream *up;
    1893          38 :         struct pim_rpf old;
    1894          38 :         enum pim_rpf_result rpf_result;
    1895             : 
    1896             :         /*
    1897             :          * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
    1898             :          */
    1899          76 :         frr_each (rb_pim_upstream, &pim->upstream_head, up) {
    1900           0 :                 if (pim_addr_is_any(up->upstream_addr)) {
    1901           0 :                         if (PIM_DEBUG_PIM_TRACE)
    1902           0 :                                 zlog_debug(
    1903             :                                         "%s: RP not configured for Upstream %s",
    1904             :                                         __func__, up->sg_str);
    1905           0 :                         continue;
    1906             :                 }
    1907             : 
    1908           0 :                 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
    1909           0 :                         if (PIM_DEBUG_PIM_TRACE)
    1910           0 :                                 zlog_debug(
    1911             :                                         "%s: Upstream %s without a path to send join, checking",
    1912             :                                         __func__, up->sg_str);
    1913           0 :                         old.source_nexthop.interface =
    1914           0 :                                 up->rpf.source_nexthop.interface;
    1915           0 :                         rpf_result = pim_rpf_update(pim, up, &old, __func__);
    1916           0 :                         if (rpf_result == PIM_RPF_CHANGED ||
    1917           0 :                                         (rpf_result == PIM_RPF_FAILURE &&
    1918           0 :                                          old.source_nexthop.interface))
    1919           0 :                                 pim_zebra_upstream_rpf_changed(pim, up, &old);
    1920             :                         /* update kernel multicast forwarding cache (MFC) */
    1921           0 :                         pim_upstream_mroute_iif_update(up->channel_oil,
    1922             :                                         __func__);
    1923             :                 }
    1924             :         }
    1925          38 :         pim_zebra_update_all_interfaces(pim);
    1926          38 : }
    1927             : 
    1928          63 : unsigned int pim_upstream_hash_key(const void *arg)
    1929             : {
    1930          63 :         const struct pim_upstream *up = arg;
    1931             : 
    1932          63 :         return pim_sgaddr_hash(up->sg, 0);
    1933             : }
    1934             : 
    1935          26 : void pim_upstream_terminate(struct pim_instance *pim)
    1936             : {
    1937          26 :         struct pim_upstream *up;
    1938             : 
    1939          70 :         while ((up = rb_pim_upstream_first(&pim->upstream_head))) {
    1940          18 :                 if (pim_upstream_del(pim, up, __func__))
    1941           3 :                         pim_upstream_timers_stop(up);
    1942             :         }
    1943             : 
    1944          26 :         rb_pim_upstream_fini(&pim->upstream_head);
    1945             : 
    1946          26 :         if (pim->upstream_sg_wheel)
    1947          26 :                 wheel_delete(pim->upstream_sg_wheel);
    1948          26 :         pim->upstream_sg_wheel = NULL;
    1949          26 : }
    1950             : 
    1951          25 : bool pim_upstream_equal(const void *arg1, const void *arg2)
    1952             : {
    1953          25 :         const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
    1954          25 :         const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
    1955             : 
    1956          25 :         return !pim_sgaddr_cmp(up1->sg, up2->sg);
    1957             : }
    1958             : 
    1959             : /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
    1960             :  * the cases where kat has to be restarted on rxing traffic -
    1961             :  *
    1962             :  * if( DirectlyConnected(S) == true AND iif == RPF_interface(S) ) {
    1963             :  * set KeepaliveTimer(S,G) to Keepalive_Period
    1964             :  * # Note: a register state transition or UpstreamJPState(S,G)
    1965             :  * # transition may happen as a result of restarting
    1966             :  * # KeepaliveTimer, and must be dealt with here.
    1967             :  * }
    1968             :  * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
    1969             :  * inherited_olist(S,G) != NULL ) {
    1970             :  * set KeepaliveTimer(S,G) to Keepalive_Period
    1971             :  * }
    1972             :  */
    1973           4 : static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
    1974             : {
    1975           4 :         struct channel_oil *c_oil = up->channel_oil;
    1976           4 :         struct interface *ifp = up->rpf.source_nexthop.interface;
    1977           4 :         struct pim_interface *pim_ifp;
    1978             : 
    1979             :         /* "iif == RPF_interface(S)" check is not easy to do as the info
    1980             :          * we get from the kernel/ASIC is really a "lookup/key hit".
    1981             :          * So we will do an approximate check here to avoid starting KAT
    1982             :          * because of (S,G,rpt) forwarding on a non-LHR.
    1983             :          */
    1984           4 :         if (!ifp)
    1985             :                 return false;
    1986             : 
    1987           4 :         pim_ifp = ifp->info;
    1988           4 :         if (pim_ifp->mroute_vif_index != *oil_parent(c_oil))
    1989             :                 return false;
    1990             : 
    1991           4 :         if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
    1992             :                                        up->sg.src)) {
    1993             :                 return true;
    1994             :         }
    1995             : 
    1996           3 :         if ((up->join_state == PIM_UPSTREAM_JOINED)
    1997           3 :                         && !pim_upstream_empty_inherited_olist(up)) {
    1998             :                 return true;
    1999             :         }
    2000             : 
    2001             :         return false;
    2002             : }
    2003             : 
    2004           4 : static bool pim_upstream_sg_running_proc(struct pim_upstream *up)
    2005             : {
    2006           4 :         bool rv = false;
    2007           4 :         struct pim_instance *pim = up->pim;
    2008             : 
    2009           4 :         if (!up->channel_oil->installed)
    2010             :                 return rv;
    2011             : 
    2012           4 :         pim_mroute_update_counters(up->channel_oil);
    2013             : 
    2014             :         // Have we seen packets?
    2015           4 :         if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt)
    2016           0 :             && (up->channel_oil->cc.lastused / 100 > 30)) {
    2017           0 :                 if (PIM_DEBUG_PIM_TRACE) {
    2018           0 :                         zlog_debug(
    2019             :                                 "%s[%s]: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
    2020             :                                 __func__, up->sg_str, pim->vrf->name,
    2021             :                                 up->channel_oil->cc.oldpktcnt,
    2022             :                                 up->channel_oil->cc.pktcnt,
    2023             :                                 up->channel_oil->cc.lastused / 100);
    2024             :                 }
    2025           0 :                 return rv;
    2026             :         }
    2027             : 
    2028           4 :         if (pim_upstream_kat_start_ok(up)) {
    2029             :                 /* Add a source reference to the stream if
    2030             :                  * one doesn't already exist */
    2031           4 :                 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
    2032           4 :                         if (PIM_DEBUG_PIM_TRACE)
    2033           0 :                                 zlog_debug(
    2034             :                                         "source reference created on kat restart %s[%s]",
    2035             :                                         up->sg_str, pim->vrf->name);
    2036             : 
    2037           4 :                         pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM,
    2038             :                                          __func__);
    2039           4 :                         PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
    2040           4 :                         pim_upstream_fhr_kat_start(up);
    2041             :                 }
    2042           4 :                 pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time);
    2043           4 :                 rv = true;
    2044           0 :         } else if (PIM_UPSTREAM_FLAG_TEST_SRC_LHR(up->flags)) {
    2045           0 :                 pim_upstream_keep_alive_timer_start(up, pim->keep_alive_time);
    2046           0 :                 rv = true;
    2047             :         }
    2048             : 
    2049           4 :         if ((up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE) &&
    2050           4 :             (up->rpf.source_nexthop.interface)) {
    2051           4 :                 pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface);
    2052           4 :                 pim_upstream_update_could_assert(up);
    2053             :         }
    2054             : 
    2055             :         return rv;
    2056             : }
    2057             : 
    2058             : /*
    2059             :  * Code to check and see if we've received packets on a S,G mroute
    2060             :  * and if so to set the SPT bit appropriately
    2061             :  */
    2062           4 : static void pim_upstream_sg_running(void *arg)
    2063             : {
    2064           4 :         struct pim_upstream *up = (struct pim_upstream *)arg;
    2065           4 :         struct pim_instance *pim = up->channel_oil->pim;
    2066             : 
    2067             :         // No packet can have arrived here if this is the case
    2068           4 :         if (!up->channel_oil->installed) {
    2069           0 :                 if (PIM_DEBUG_TRACE)
    2070           0 :                         zlog_debug("%s: %s%s is not installed in mroute",
    2071             :                                    __func__, up->sg_str, pim->vrf->name);
    2072           0 :                 return;
    2073             :         }
    2074             : 
    2075             :         /*
    2076             :          * This is a bit of a hack
    2077             :          * We've noted that we should rescan but
    2078             :          * we've missed the window for doing so in
    2079             :          * pim_zebra.c for some reason.  I am
    2080             :          * only doing this at this point in time
    2081             :          * to get us up and working for the moment
    2082             :          */
    2083           4 :         if (up->channel_oil->oil_inherited_rescan) {
    2084           0 :                 if (PIM_DEBUG_TRACE)
    2085           0 :                         zlog_debug(
    2086             :                                 "%s: Handling unscanned inherited_olist for %s[%s]",
    2087             :                                 __func__, up->sg_str, pim->vrf->name);
    2088           0 :                 pim_upstream_inherited_olist_decide(pim, up);
    2089           0 :                 up->channel_oil->oil_inherited_rescan = 0;
    2090             :         }
    2091             : 
    2092           4 :         pim_upstream_sg_running_proc(up);
    2093             : }
    2094             : 
    2095           0 : void pim_upstream_add_lhr_star_pimreg(struct pim_instance *pim)
    2096             : {
    2097           0 :         struct pim_upstream *up;
    2098             : 
    2099           0 :         frr_each (rb_pim_upstream, &pim->upstream_head, up) {
    2100           0 :                 if (!pim_addr_is_any(up->sg.src))
    2101           0 :                         continue;
    2102             : 
    2103           0 :                 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags))
    2104           0 :                         continue;
    2105             : 
    2106           0 :                 pim_channel_add_oif(up->channel_oil, pim->regiface,
    2107             :                                     PIM_OIF_FLAG_PROTO_GM, __func__);
    2108             :         }
    2109           0 : }
    2110             : 
    2111           3 : void pim_upstream_spt_prefix_list_update(struct pim_instance *pim,
    2112             :                                          struct prefix_list *pl)
    2113             : {
    2114           3 :         const char *pname = prefix_list_name(pl);
    2115             : 
    2116           3 :         if (pim->spt.plist && strcmp(pim->spt.plist, pname) == 0) {
    2117           0 :                 pim_upstream_remove_lhr_star_pimreg(pim, pname);
    2118             :         }
    2119           3 : }
    2120             : 
    2121             : /*
    2122             :  * nlist -> The new prefix list
    2123             :  *
    2124             :  * Per Group Application of pimreg to the OIL
    2125             :  * If the prefix list tells us DENY then
    2126             :  * we need to Switchover to SPT immediate
    2127             :  * so add the pimreg.
    2128             :  * If the prefix list tells us to ACCEPT than
    2129             :  * we need to Never do the SPT so remove
    2130             :  * the interface
    2131             :  *
    2132             :  */
    2133           0 : void pim_upstream_remove_lhr_star_pimreg(struct pim_instance *pim,
    2134             :                                          const char *nlist)
    2135             : {
    2136           0 :         struct pim_upstream *up;
    2137           0 :         struct prefix_list *np;
    2138           0 :         struct prefix g;
    2139           0 :         enum prefix_list_type apply_new;
    2140             : 
    2141           0 :         np = prefix_list_lookup(PIM_AFI, nlist);
    2142             : 
    2143           0 :         frr_each (rb_pim_upstream, &pim->upstream_head, up) {
    2144           0 :                 if (!pim_addr_is_any(up->sg.src))
    2145           0 :                         continue;
    2146             : 
    2147           0 :                 if (!PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags))
    2148           0 :                         continue;
    2149             : 
    2150           0 :                 if (!nlist) {
    2151           0 :                         pim_channel_del_oif(up->channel_oil, pim->regiface,
    2152             :                                             PIM_OIF_FLAG_PROTO_GM, __func__);
    2153           0 :                         continue;
    2154             :                 }
    2155           0 :                 pim_addr_to_prefix(&g, up->sg.grp);
    2156           0 :                 apply_new = prefix_list_apply_ext(np, NULL, &g, true);
    2157           0 :                 if (apply_new == PREFIX_DENY)
    2158           0 :                         pim_channel_add_oif(up->channel_oil, pim->regiface,
    2159             :                                             PIM_OIF_FLAG_PROTO_GM, __func__);
    2160             :                 else
    2161           0 :                         pim_channel_del_oif(up->channel_oil, pim->regiface,
    2162             :                                             PIM_OIF_FLAG_PROTO_GM, __func__);
    2163             :         }
    2164           0 : }
    2165             : 
    2166          26 : void pim_upstream_init(struct pim_instance *pim)
    2167             : {
    2168          26 :         char name[64];
    2169             : 
    2170          26 :         snprintf(name, sizeof(name), "PIM %s Timer Wheel", pim->vrf->name);
    2171          52 :         pim->upstream_sg_wheel =
    2172          26 :                 wheel_init(router->master, 31000, 100, pim_upstream_hash_key,
    2173             :                            pim_upstream_sg_running, name);
    2174             : 
    2175          26 :         rb_pim_upstream_init(&pim->upstream_head);
    2176          26 : }

Generated by: LCOV version v1.16-topotato