3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
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
32 #include "pim_instance.h"
34 #include "pim_iface.h"
35 #include "pim_ifchannel.h"
36 #include "pim_zebra.h"
42 #include "pim_macro.h"
44 #include "pim_upstream.h"
49 RB_GENERATE(pim_ifchannel_rb
, pim_ifchannel
, pim_ifp_rb
, pim_ifchannel_compare
);
51 int pim_ifchannel_compare(const struct pim_ifchannel
*ch1
,
52 const struct pim_ifchannel
*ch2
)
54 struct pim_interface
*pim_ifp1
;
55 struct pim_interface
*pim_ifp2
;
57 pim_ifp1
= ch1
->interface
->info
;
58 pim_ifp2
= ch2
->interface
->info
;
60 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
63 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
66 return pim_sgaddr_cmp(ch1
->sg
, ch2
->sg
);
70 * A (*,G) or a (*,*) is going away
71 * remove the parent pointer from
72 * those pointing at us
74 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
76 struct pim_ifchannel
*child
;
81 while (!list_isempty(ch
->sources
)) {
82 child
= listnode_head(ch
->sources
);
84 listnode_delete(ch
->sources
, child
);
89 * A (*,G) or a (*,*) is being created
90 * find all the children that would point
93 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
95 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
96 struct pim_ifchannel
*child
;
98 // Basic Sanity that we are not being silly
99 if (!pim_addr_is_any(ch
->sg
.src
) && !pim_addr_is_any(ch
->sg
.grp
))
102 if (pim_addr_is_any(ch
->sg
.src
) && pim_addr_is_any(ch
->sg
.grp
))
105 RB_FOREACH (child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
106 if (!pim_addr_is_any(ch
->sg
.grp
) &&
107 !pim_addr_cmp(child
->sg
.grp
, ch
->sg
.grp
) && (child
!= ch
)) {
109 listnode_add_sort(ch
->sources
, child
);
114 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
116 struct pim_interface
*pim_ifp
;
117 struct pim_upstream
*up
;
119 pim_ifp
= ch
->interface
->info
;
121 if (PIM_DEBUG_PIM_TRACE
)
122 zlog_debug("%s: ifchannel entry %s(%s) del start", __func__
,
123 ch
->sg_str
, ch
->interface
->name
);
125 if (PIM_I_am_DualActive(pim_ifp
)) {
128 "%s: if-chnanel-%s is deleted from a Dual active Interface",
129 __func__
, ch
->sg_str
);
130 /* Post Delete only if it is the last Dual-active Interface */
131 if (ch
->upstream
->dualactive_ifchannel_count
== 1) {
132 pim_mlag_up_local_del(pim_ifp
->pim
, ch
->upstream
);
133 PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
134 ch
->upstream
->flags
);
136 ch
->upstream
->dualactive_ifchannel_count
--;
139 if (ch
->upstream
->channel_oil
) {
140 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
141 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
142 mask
|= PIM_OIF_FLAG_PROTO_GM
;
145 * A S,G RPT channel can have an empty oil, we also
146 * need to take into account the fact that a ifchannel
147 * might have been suppressing a *,G ifchannel from
148 * being inherited. So let's figure out what
149 * needs to be done here
151 if (!pim_addr_is_any(ch
->sg
.src
) &&
152 pim_upstream_evaluate_join_desired_interface(
153 ch
->upstream
, ch
, ch
->parent
))
154 pim_channel_add_oif(ch
->upstream
->channel_oil
,
156 PIM_OIF_FLAG_PROTO_STAR
,
159 pim_channel_del_oif(ch
->upstream
->channel_oil
,
160 ch
->interface
, mask
, __func__
);
162 * Do we have any S,G's that are inheriting?
163 * Nuke from on high too.
165 if (ch
->upstream
->sources
) {
166 struct pim_upstream
*child
;
167 struct listnode
*up_node
;
169 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
171 pim_channel_del_inherited_oif(
179 * When this channel is removed
180 * we need to find all our children
181 * and make sure our pointers are fixed
183 pim_ifchannel_remove_children(ch
);
186 list_delete(&ch
->sources
);
188 listnode_delete(ch
->upstream
->ifchannels
, ch
);
192 /* upstream is common across ifchannels, check if upstream's
193 ifchannel list is empty before deleting upstream_del
194 ref count will take care of it.
196 if (ch
->upstream
->ref_count
> 0)
197 up
= pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __func__
);
200 if (PIM_DEBUG_PIM_TRACE
)
202 "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
203 __func__
, ch
->upstream
->ref_count
,
204 ch
->interface
->name
, ch
->sg_str
);
209 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
210 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
211 THREAD_OFF(ch
->t_ifassert_timer
);
214 listnode_delete(ch
->parent
->sources
, ch
);
218 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
220 if (PIM_DEBUG_PIM_TRACE
)
221 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__
,
222 ch
->sg_str
, ch
->interface
->name
);
224 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
227 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
230 void pim_ifchannel_delete_all(struct interface
*ifp
)
232 struct pim_interface
*pim_ifp
;
233 struct pim_ifchannel
*ch
;
239 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
240 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
242 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
243 pim_ifchannel_delete(ch
);
247 void delete_on_noinfo(struct pim_ifchannel
*ch
)
249 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
250 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
251 && ch
->t_ifjoin_expiry_timer
== NULL
)
252 pim_ifchannel_delete(ch
);
255 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
256 enum pim_ifjoin_state new_state
)
258 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
259 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
260 struct pim_ifchannel
*child_ch
;
262 if (PIM_DEBUG_PIM_EVENTS
)
264 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
265 ch
->interface
->name
, ch
->sg_str
,
266 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
267 pim_ifchannel_ifjoin_name(new_state
, 0));
270 if (old_state
== new_state
) {
271 if (PIM_DEBUG_PIM_EVENTS
) {
273 "%s called by %s: non-transition on state %d (%s)",
274 __func__
, caller
, new_state
,
275 pim_ifchannel_ifjoin_name(new_state
, 0));
280 ch
->ifjoin_state
= new_state
;
282 if (pim_addr_is_any(ch
->sg
.src
)) {
283 struct pim_upstream
*up
= ch
->upstream
;
284 struct pim_upstream
*child
;
285 struct listnode
*up_node
;
288 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
289 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
291 struct channel_oil
*c_oil
=
294 if (PIM_DEBUG_PIM_TRACE
)
296 "%s %s: Prune(S,G)=%s from %s",
304 * If the S,G has no if channel and the
306 * has output here then the *,G was
307 * supplying the implied
308 * if channel. So remove it.
310 if (oil_if_has(c_oil
,
311 pim_ifp
->mroute_vif_index
))
312 pim_channel_del_inherited_oif(
313 c_oil
, ch
->interface
,
317 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
318 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
320 if (PIM_DEBUG_PIM_TRACE
)
322 "%s %s: Join(S,G)=%s from %s",
327 /* check if the channel can be
328 * inherited into the SG's OIL
330 child_ch
= pim_ifchannel_find(
333 if (pim_upstream_eval_inherit_if(
334 child
, child_ch
, ch
)) {
338 PIM_OIF_FLAG_PROTO_STAR
,
340 pim_upstream_update_join_desired(
341 pim_ifp
->pim
, child
);
347 /* Transition to/from NOINFO ? */
348 if ((old_state
== PIM_IFJOIN_NOINFO
)
349 || (new_state
== PIM_IFJOIN_NOINFO
)) {
351 if (PIM_DEBUG_PIM_EVENTS
) {
352 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
353 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
355 ch
->sg_str
, ch
->interface
->name
);
359 Record uptime of state transition to/from NOINFO
361 ch
->ifjoin_creation
= pim_time_monotonic_sec();
363 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
364 pim_ifchannel_update_could_assert(ch
);
365 pim_ifchannel_update_assert_tracking_desired(ch
);
369 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
372 switch (ifjoin_state
) {
373 case PIM_IFJOIN_NOINFO
:
374 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
378 case PIM_IFJOIN_JOIN
:
380 case PIM_IFJOIN_PRUNE
:
381 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
385 case PIM_IFJOIN_PRUNE_PENDING
:
386 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
390 case PIM_IFJOIN_PRUNE_TMP
:
391 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
395 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
396 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
402 return "ifjoin_bad_state";
405 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
407 switch (ifassert_state
) {
408 case PIM_IFASSERT_NOINFO
:
410 case PIM_IFASSERT_I_AM_WINNER
:
412 case PIM_IFASSERT_I_AM_LOSER
:
416 return "ifassert_bad_state";
420 RFC 4601: 4.6.5. Assert State Macros
422 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
423 defaults to Infinity when in the NoInfo state.
425 void reset_ifassert_state(struct pim_ifchannel
*ch
)
427 THREAD_OFF(ch
->t_ifassert_timer
);
429 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, PIMADDR_ANY
,
430 router
->infinite_assert_metric
);
433 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
, pim_sgaddr
*sg
)
435 struct pim_interface
*pim_ifp
;
436 struct pim_ifchannel
*ch
;
437 struct pim_ifchannel lookup
;
442 zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
443 __func__
, sg
, ifp
->name
);
448 lookup
.interface
= ifp
;
449 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
454 static void ifmembership_set(struct pim_ifchannel
*ch
,
455 enum pim_ifmembership membership
)
457 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
459 if (ch
->local_ifmembership
== membership
)
462 if (PIM_DEBUG_PIM_EVENTS
) {
463 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
464 __func__
, ch
->sg_str
,
465 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
467 ch
->interface
->name
);
470 ch
->local_ifmembership
= membership
;
472 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
473 pim_ifchannel_update_could_assert(ch
);
474 pim_ifchannel_update_assert_tracking_desired(ch
);
478 void pim_ifchannel_membership_clear(struct interface
*ifp
)
480 struct pim_interface
*pim_ifp
;
481 struct pim_ifchannel
*ch
;
486 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
487 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
490 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
492 struct pim_interface
*pim_ifp
;
493 struct pim_ifchannel
*ch
, *ch_tmp
;
498 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
499 delete_on_noinfo(ch
);
503 * For a given Interface, if we are given a S,G
504 * Find the *,G (If we have it).
505 * If we are passed a *,G, find the *,* ifchannel
508 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
510 pim_sgaddr parent_sg
= ch
->sg
;
511 struct pim_ifchannel
*parent
= NULL
;
514 if (!pim_addr_is_any(parent_sg
.src
) &&
515 !pim_addr_is_any(parent_sg
.grp
)) {
516 parent_sg
.src
= PIMADDR_ANY
;
517 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
520 listnode_add(parent
->sources
, ch
);
527 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
, pim_sgaddr
*sg
,
528 uint8_t source_flags
, int up_flags
)
530 struct pim_interface
*pim_ifp
;
531 struct pim_ifchannel
*ch
;
532 struct pim_upstream
*up
;
534 ch
= pim_ifchannel_find(ifp
, sg
);
536 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
537 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
539 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
540 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
543 ch
->upstream
->flags
|= up_flags
;
544 else if (PIM_DEBUG_EVENTS
)
545 zlog_debug("%s:%pSG No Upstream found", __func__
, sg
);
552 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
555 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
556 && !(source_flags
& PIM_ENCODE_WC_BIT
))
557 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
561 snprintfrr(ch
->sg_str
, sizeof(ch
->sg_str
), "%pSG", sg
);
562 ch
->parent
= pim_ifchannel_find_parent(ch
);
563 if (pim_addr_is_any(ch
->sg
.src
)) {
564 ch
->sources
= list_new();
566 (int (*)(void *, void *))pim_ifchannel_compare
;
570 pim_ifchannel_find_new_children(ch
);
571 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
573 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
574 ch
->t_ifjoin_expiry_timer
= NULL
;
575 ch
->t_ifjoin_prune_pending_timer
= NULL
;
576 ch
->ifjoin_creation
= 0;
578 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
580 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
, __func__
, ch
);
584 listnode_add_sort(up
->ifchannels
, ch
);
586 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
587 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
589 ch
->ifassert_winner
= PIMADDR_ANY
;
592 ch
->t_ifassert_timer
= NULL
;
593 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
594 reset_ifassert_state(ch
);
595 if (pim_macro_ch_could_assert_eval(ch
))
596 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
598 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
600 if (pim_macro_assert_tracking_desired_eval(ch
))
601 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
603 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
606 * advertise MLAG Data to MLAG peer
608 if (PIM_I_am_DualActive(pim_ifp
)) {
609 up
->dualactive_ifchannel_count
++;
610 /* Sync once for upstream */
611 if (up
->dualactive_ifchannel_count
== 1) {
612 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up
->flags
);
613 pim_mlag_up_local_add(pim_ifp
->pim
, up
);
617 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
618 __func__
, up
->sg_str
,
619 up
->dualactive_ifchannel_count
, up
->flags
);
622 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
623 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
625 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
626 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
628 if (PIM_DEBUG_PIM_TRACE
)
629 zlog_debug("%s: ifchannel %s(%s) is created ", __func__
,
630 ch
->sg_str
, ch
->interface
->name
);
635 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
637 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
638 pim_forward_stop(ch
);
641 PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch
->upstream
->flags
);
643 PIM_IF_FLAG_UNSET_PROTO_PIM(ch
->flags
);
645 delete_on_noinfo(ch
);
648 static void on_ifjoin_expiry_timer(struct thread
*t
)
650 struct pim_ifchannel
*ch
;
654 if (PIM_DEBUG_PIM_TRACE
)
655 zlog_debug("%s: ifchannel %s expiry timer", __func__
,
658 ifjoin_to_noinfo(ch
);
659 /* ch may have been deleted */
662 static void on_ifjoin_prune_pending_timer(struct thread
*t
)
664 struct pim_ifchannel
*ch
;
665 int send_prune_echo
; /* boolean */
666 struct interface
*ifp
;
667 struct pim_interface
*pim_ifp
;
671 if (PIM_DEBUG_PIM_TRACE
)
672 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
674 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
676 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
679 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
680 /* Send PruneEcho(S,G) ? */
682 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
684 if (send_prune_echo
) {
687 rpf
.source_nexthop
.interface
= ifp
;
688 pim_addr_to_prefix(&rpf
.rpf_addr
,
689 pim_ifp
->primary_address
);
690 pim_jp_agg_single_upstream_send(
691 &rpf
, ch
->upstream
, 0);
694 ifjoin_to_noinfo(ch
);
696 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
697 * message on RP path upon prune timer expiry.
699 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
701 struct pim_upstream
*parent
=
702 ch
->upstream
->parent
;
704 pim_upstream_update_join_desired(pim_ifp
->pim
,
707 pim_jp_agg_single_upstream_send(&parent
->rpf
,
710 * SGRpt prune pending expiry has to install
711 * SG entry with empty olist to drop the SG
712 * traffic incase no other intf exists.
713 * On that scenario, SG entry wouldn't have
714 * got installed until Prune pending timer
715 * expired. So install now.
718 ch
->upstream
->channel_oil
, ifp
,
719 PIM_OIF_FLAG_PROTO_STAR
, __func__
);
720 if (!ch
->upstream
->channel_oil
->installed
)
721 pim_upstream_mroute_add(
722 ch
->upstream
->channel_oil
,
726 /* from here ch may have been deleted */
730 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
731 pim_addr upstream
, pim_sgaddr
*sg
,
732 uint8_t source_flags
, int holdtime
)
734 struct pim_upstream
*up
;
735 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
738 /* Upstream (S,G) in Joined state ? */
739 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
742 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
745 /* Upstream (S,G) in Joined state */
747 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
748 /* RPF'(S,G) not found */
749 zlog_warn("%s %s: RPF'%s not found", __FILE__
, __func__
,
754 rpf_addr
= pim_addr_from_prefix(&up
->rpf
.rpf_addr
);
756 /* upstream directed to RPF'(S,G) ? */
757 if (pim_addr_cmp(upstream
, rpf_addr
)) {
759 "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
760 __FILE__
, __func__
, up
->sg_str
, &upstream
, &rpf_addr
,
764 /* upstream directed to RPF'(S,G) */
767 /* Join(S,G) to RPF'(S,G) */
768 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
, holdtime
);
772 /* Prune to RPF'(S,G) */
774 if (source_flags
& PIM_RPT_BIT_MASK
) {
775 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
776 /* Prune(*,G) to RPF'(S,G) */
777 pim_upstream_join_timer_decrease_to_t_override(
782 /* Prune(S,G,rpt) to RPF'(S,G) */
783 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
788 /* Prune(S,G) to RPF'(S,G) */
789 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
792 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
793 pim_addr upstream
, pim_sgaddr
*sg
,
794 uint8_t source_flags
, uint16_t holdtime
)
796 struct pim_interface
*recv_pim_ifp
;
797 int is_local
; /* boolean */
799 recv_pim_ifp
= recv_ifp
->info
;
800 assert(recv_pim_ifp
);
802 is_local
= !pim_addr_cmp(upstream
, recv_pim_ifp
->primary_address
);
807 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
809 "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
810 __func__
, is_join
? "join" : "prune", sg
, &upstream
,
814 * Since recv upstream addr was not directed to our primary
815 * address, check if we should react to it in any way.
817 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
820 return 1; /* non-local */
823 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel
*ch
,
824 struct pim_interface
*pim_ifp
)
826 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
827 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
828 /* check if the interface qualifies as an immediate
831 if (pim_upstream_evaluate_join_desired_interface(
834 pim_channel_add_oif(ch
->upstream
->channel_oil
,
836 PIM_OIF_FLAG_PROTO_PIM
,
838 pim_upstream_update_join_desired(pim_ifp
->pim
,
844 void pim_ifchannel_join_add(struct interface
*ifp
, pim_addr neigh_addr
,
845 pim_addr upstream
, pim_sgaddr
*sg
,
846 uint8_t source_flags
, uint16_t holdtime
)
848 struct pim_interface
*pim_ifp
;
849 struct pim_ifchannel
*ch
;
851 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
856 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
857 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
860 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
862 Transitions from "I am Assert Loser" State
864 Receive Join(S,G) on Interface I
866 We receive a Join(S,G) that has the Upstream Neighbor Address
867 field set to my primary IP address on interface I. The action is
868 to transition to NoInfo state, delete this (S,G) assert state
869 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
872 Notice: The nonlocal_upstream() test above ensures the upstream
873 address of the join message is our primary address.
875 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
876 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
877 __func__
, ch
->sg_str
, &neigh_addr
, ifp
->name
);
879 assert_action_a5(ch
);
885 switch (ch
->ifjoin_state
) {
886 case PIM_IFJOIN_NOINFO
:
887 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
888 if (pim_macro_chisin_oiflist(ch
)) {
889 pim_upstream_inherited_olist(pim_ifp
->pim
,
891 pim_forward_start(ch
);
894 * If we are going to be a LHR, we need to note it
896 if (ch
->upstream
->parent
&&
897 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
898 ch
->upstream
->parent
->flags
))
899 && !(ch
->upstream
->flags
900 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
901 pim_upstream_ref(ch
->upstream
,
902 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
904 pim_upstream_keep_alive_timer_start(
905 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
908 case PIM_IFJOIN_JOIN
:
909 assert(!ch
->t_ifjoin_prune_pending_timer
);
912 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
914 previously received join message with holdtime=0xFFFF.
916 if (ch
->t_ifjoin_expiry_timer
) {
917 unsigned long remain
= thread_timer_remain_second(
918 ch
->t_ifjoin_expiry_timer
);
919 if (remain
> holdtime
) {
921 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
924 Transitions from Join State
926 The (S,G) downstream state machine on
927 interface I remains in
928 Join state, and the Expiry Timer (ET) is
930 maximum of its current value and the HoldTime
932 triggering Join/Prune message.
934 Conclusion: Do not change the ET if the
936 higher than the received join holdtime.
941 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
943 case PIM_IFJOIN_PRUNE
:
944 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
945 pim_ifchannel_ifjoin_switch(__func__
, ch
,
947 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
948 delete_on_noinfo(ch
);
951 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
953 case PIM_IFJOIN_PRUNE_PENDING
:
955 * Transitions from Prune-Pending State (Receive Join)
956 * RFC 7761 Sec 4.5.2:
957 * The (S,G) downstream state machine on interface I
958 * transitions to the Join state. The Prune-Pending Timer is
959 * canceled (without triggering an expiry event). The
960 * Expiry Timer (ET) is restarted and is then set to the
961 * maximum of its current value and the HoldTime from the
962 * triggering Join/Prune message.
964 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
966 /* Check if SGRpt join Received */
967 if ((source_flags
& PIM_ENCODE_RPT_BIT
) &&
968 !pim_addr_is_any(sg
->src
)) {
970 * Transitions from Prune-Pending State (Rcv SGRpt Join)
971 * RFC 7761 Sec 4.5.3:
972 * The (S,G,rpt) downstream state machine on interface
973 * I transitions to the NoInfo state.The ET and PPT are
976 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
977 pim_ifchannel_ifjoin_switch(__func__
, ch
,
982 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
984 if (ch
->t_ifjoin_expiry_timer
) {
985 unsigned long remain
= thread_timer_remain_second(
986 ch
->t_ifjoin_expiry_timer
);
988 if (remain
> holdtime
)
991 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
994 case PIM_IFJOIN_PRUNE_TMP
:
996 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1000 if (holdtime
!= 0xFFFF) {
1001 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
1002 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1006 void pim_ifchannel_prune(struct interface
*ifp
, pim_addr upstream
,
1007 pim_sgaddr
*sg
, uint8_t source_flags
,
1010 struct pim_ifchannel
*ch
;
1011 struct pim_interface
*pim_ifp
;
1012 int jp_override_interval_msec
;
1014 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
1019 ch
= pim_ifchannel_find(ifp
, sg
);
1020 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
1021 if (PIM_DEBUG_PIM_TRACE
)
1022 zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1023 __func__
, ifp
->name
, sg
,
1028 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
1029 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
1031 pim_ifp
= ifp
->info
;
1033 switch (ch
->ifjoin_state
) {
1034 case PIM_IFJOIN_NOINFO
:
1035 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1036 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
1037 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
1039 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1040 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1041 jp_override_interval_msec
=
1042 pim_if_jp_override_interval_msec(ifp
);
1044 jp_override_interval_msec
=
1045 0; /* schedule to expire immediately */
1046 /* If we called ifjoin_prune() directly instead, care
1048 be taken not to use "ch" afterwards since it would be
1051 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1052 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1053 thread_add_timer_msec(
1054 router
->master
, on_ifjoin_prune_pending_timer
,
1055 ch
, jp_override_interval_msec
,
1056 &ch
->t_ifjoin_prune_pending_timer
);
1057 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1059 &ch
->t_ifjoin_expiry_timer
);
1060 pim_upstream_update_join_desired(pim_ifp
->pim
,
1064 case PIM_IFJOIN_PRUNE_PENDING
:
1067 case PIM_IFJOIN_JOIN
:
1069 * The (S,G) downstream state machine on interface I
1070 * transitions to the Prune-Pending state. The
1071 * Prune-Pending Timer is started. It is set to the
1072 * J/P_Override_Interval(I) if the router has more than one
1073 * neighbor on that interface; otherwise, it is set to zero,
1074 * causing it to expire immediately.
1077 pim_ifchannel_ifjoin_switch(__func__
, ch
,
1078 PIM_IFJOIN_PRUNE_PENDING
);
1080 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1081 jp_override_interval_msec
=
1082 pim_if_jp_override_interval_msec(ifp
);
1084 jp_override_interval_msec
=
1085 0; /* schedule to expire immediately */
1086 /* If we called ifjoin_prune() directly instead, care should
1087 be taken not to use "ch" afterwards since it would be
1089 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1090 thread_add_timer_msec(router
->master
,
1091 on_ifjoin_prune_pending_timer
, ch
,
1092 jp_override_interval_msec
,
1093 &ch
->t_ifjoin_prune_pending_timer
);
1095 case PIM_IFJOIN_PRUNE
:
1096 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1097 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1099 * While in Prune State, Receive SGRpt Prune.
1100 * RFC 7761 Sec 4.5.3:
1101 * The (S,G,rpt) downstream state machine on interface I
1102 * remains in Prune state. The Expiry Timer (ET) is
1103 * restarted and is then set to the maximum of its
1104 * current value and the HoldTime from the triggering
1105 * Join/Prune message.
1107 if (ch
->t_ifjoin_expiry_timer
) {
1108 unsigned long rem
= thread_timer_remain_second(
1109 ch
->t_ifjoin_expiry_timer
);
1113 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1116 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1118 &ch
->t_ifjoin_expiry_timer
);
1121 case PIM_IFJOIN_PRUNE_TMP
:
1122 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1123 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1124 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1125 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1127 &ch
->t_ifjoin_expiry_timer
);
1130 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1131 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1132 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1133 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1134 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1136 &ch
->t_ifjoin_expiry_timer
);
1142 int pim_ifchannel_local_membership_add(struct interface
*ifp
, pim_sgaddr
*sg
,
1145 struct pim_ifchannel
*ch
, *starch
;
1146 struct pim_interface
*pim_ifp
;
1147 struct pim_instance
*pim
;
1150 /* PIM enabled on interface? */
1151 pim_ifp
= ifp
->info
;
1153 if (PIM_DEBUG_EVENTS
)
1154 zlog_debug("%s:%pSG Expected pim interface setup for %s",
1155 __func__
, sg
, ifp
->name
);
1159 if (!pim_ifp
->pim_enable
) {
1160 if (PIM_DEBUG_EVENTS
)
1161 zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1162 __func__
, sg
, ifp
->name
);
1168 /* skip (*,G) ch creation if G is of type SSM */
1169 if (pim_addr_is_any(sg
->src
)) {
1170 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1171 if (PIM_DEBUG_PIM_EVENTS
)
1172 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1178 /* vxlan term mroutes use ipmr-lo as local member to
1179 * pull down multicast vxlan tunnel traffic
1181 up_flags
= is_vxlan
? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
:
1182 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
;
1183 ch
= pim_ifchannel_add(ifp
, sg
, 0, up_flags
);
1185 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1187 if (pim_addr_is_any(sg
->src
)) {
1188 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1189 struct pim_upstream
*child
;
1190 struct listnode
*up_node
;
1194 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1195 if (PIM_DEBUG_EVENTS
)
1196 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1197 __FILE__
, __func__
, child
->sg_str
,
1198 ifp
->name
, up
->sg_str
);
1200 if (!child
->rpf
.source_nexthop
.interface
) {
1201 /* when iif unknown, do not inherit */
1202 if (PIM_DEBUG_EVENTS
)
1204 "Skipped (S,G)=%s(%s) from %s: no iif",
1205 child
->sg_str
, ifp
->name
,
1210 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1211 if (pim_upstream_evaluate_join_desired_interface(
1212 child
, ch
, starch
)) {
1213 pim_channel_add_oif(child
->channel_oil
, ifp
,
1214 PIM_OIF_FLAG_PROTO_STAR
,
1216 pim_upstream_update_join_desired(pim
, child
);
1220 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1221 if (pim
->spt
.plist
) {
1222 struct prefix_list
*plist
= prefix_list_lookup(
1223 AFI_IP
, pim
->spt
.plist
);
1226 pim_addr_to_prefix(&g
, up
->sg
.grp
);
1227 if (prefix_list_apply(plist
, &g
)
1229 pim_channel_add_oif(
1230 up
->channel_oil
, pim
->regiface
,
1231 PIM_OIF_FLAG_PROTO_GM
,
1236 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1237 PIM_OIF_FLAG_PROTO_GM
, __func__
);
1243 void pim_ifchannel_local_membership_del(struct interface
*ifp
, pim_sgaddr
*sg
)
1245 struct pim_ifchannel
*starch
, *ch
, *orig
;
1246 struct pim_interface
*pim_ifp
;
1248 /* PIM enabled on interface? */
1249 pim_ifp
= ifp
->info
;
1252 if (!pim_ifp
->pim_enable
)
1255 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1258 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1260 if (pim_addr_is_any(sg
->src
)) {
1261 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1262 struct pim_upstream
*child
;
1263 struct listnode
*up_node
, *up_nnode
;
1267 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1268 struct channel_oil
*c_oil
= child
->channel_oil
;
1269 struct pim_ifchannel
*chchannel
=
1270 pim_ifchannel_find(ifp
, &child
->sg
);
1272 pim_ifp
= ifp
->info
;
1274 if (PIM_DEBUG_EVENTS
)
1275 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1276 __FILE__
, __func__
, up
->sg_str
,
1277 ifp
->name
, child
->sg_str
);
1279 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1281 * If the S,G has no if channel and the c_oil still
1282 * has output here then the *,G was supplying the
1284 * if channel. So remove it.
1286 if (!pim_upstream_evaluate_join_desired_interface(
1287 child
, ch
, starch
) ||
1289 oil_if_has(c_oil
, pim_ifp
->mroute_vif_index
))) {
1290 pim_channel_del_inherited_oif(c_oil
, ifp
,
1294 /* Child node removal/ref count-- will happen as part of
1295 * parent' delete_no_info */
1299 /* Resettng the IGMP flags here */
1301 PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig
->upstream
->flags
);
1303 PIM_IF_FLAG_UNSET_PROTO_IGMP(orig
->flags
);
1305 delete_on_noinfo(orig
);
1308 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1310 int old_couldassert
=
1311 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1312 int new_couldassert
=
1313 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1315 if (new_couldassert
== old_couldassert
)
1318 if (PIM_DEBUG_PIM_EVENTS
)
1319 zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1320 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
,
1321 ch
->interface
->name
, old_couldassert
,
1324 if (new_couldassert
) {
1325 /* CouldAssert(S,G,I) switched from false to true */
1326 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1328 /* CouldAssert(S,G,I) switched from true to false */
1329 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1331 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1332 assert_action_a4(ch
);
1336 pim_ifchannel_update_my_assert_metric(ch
);
1340 my_assert_metric may be affected by:
1343 pim_ifp->primary_address
1344 rpf->source_nexthop.mrib_metric_preference;
1345 rpf->source_nexthop.mrib_route_metric;
1347 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1349 struct pim_assert_metric my_metric_new
=
1350 pim_macro_ch_my_assert_metric_eval(ch
);
1352 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1355 if (PIM_DEBUG_PIM_EVENTS
)
1357 "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1358 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1359 ch
->ifassert_my_metric
.rpt_bit_flag
,
1360 ch
->ifassert_my_metric
.metric_preference
,
1361 ch
->ifassert_my_metric
.route_metric
,
1362 &ch
->ifassert_my_metric
.ip_address
,
1363 my_metric_new
.rpt_bit_flag
,
1364 my_metric_new
.metric_preference
,
1365 my_metric_new
.route_metric
, &my_metric_new
.ip_address
);
1367 ch
->ifassert_my_metric
= my_metric_new
;
1369 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1370 &ch
->ifassert_winner_metric
)) {
1371 assert_action_a5(ch
);
1375 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1377 int old_atd
= PIM_FORCE_BOOLEAN(
1378 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1380 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1382 if (new_atd
== old_atd
)
1385 if (PIM_DEBUG_PIM_EVENTS
)
1387 "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1388 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1392 /* AssertTrackingDesired(S,G,I) switched from false to true */
1393 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1395 /* AssertTrackingDesired(S,G,I) switched from true to false */
1396 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1398 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1399 assert_action_a5(ch
);
1405 * If we have a new pim interface, check to
1406 * see if any of the pre-existing channels have
1407 * their upstream out that way and turn on forwarding
1408 * for that ifchannel then.
1410 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1412 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1413 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1414 struct interface
*ifp
;
1416 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1417 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1418 struct pim_ifchannel
*ch
;
1423 if (new_pim_ifp
== loop_pim_ifp
)
1426 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1427 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1428 struct pim_upstream
*up
= ch
->upstream
;
1429 if ((!up
->channel_oil
)
1430 && (up
->rpf
.source_nexthop
1431 .interface
== new_ifp
))
1432 pim_forward_start(ch
);
1439 * Downstream per-interface (S,G,rpt) state machine
1440 * states that we need to move (S,G,rpt) items
1441 * into different states at the start of the
1442 * reception of a *,G join as well, when
1443 * we get End of Message
1445 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1448 bool send_upstream_starg
= false;
1449 struct pim_ifchannel
*child
;
1450 struct listnode
*ch_node
, *nch_node
;
1451 struct pim_instance
*pim
=
1452 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1453 struct pim_upstream
*starup
= ch
->upstream
;
1455 if (PIM_DEBUG_PIM_TRACE
)
1457 "%s: %s %s eom: %d join %u", __func__
,
1458 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1459 ch
->sg_str
, eom
, join
);
1463 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1464 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1467 switch (child
->ifjoin_state
) {
1468 case PIM_IFJOIN_NOINFO
:
1469 case PIM_IFJOIN_JOIN
:
1471 case PIM_IFJOIN_PRUNE
:
1473 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1475 case PIM_IFJOIN_PRUNE_PENDING
:
1477 child
->ifjoin_state
=
1478 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1480 case PIM_IFJOIN_PRUNE_TMP
:
1481 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1485 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1486 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1487 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1489 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1490 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1492 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1493 (!pim_upstream_empty_inherited_olist(
1494 child
->upstream
))) {
1495 pim_channel_add_oif(
1496 child
->upstream
->channel_oil
,
1497 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
,
1499 pim_upstream_update_join_desired(pim
,
1502 send_upstream_starg
= true;
1504 delete_on_noinfo(child
);
1509 if (send_upstream_starg
)
1510 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);