1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
19 #include "pim_instance.h"
21 #include "pim_iface.h"
22 #include "pim_ifchannel.h"
23 #include "pim_zebra.h"
29 #include "pim_macro.h"
31 #include "pim_upstream.h"
36 RB_GENERATE(pim_ifchannel_rb
, pim_ifchannel
, pim_ifp_rb
, pim_ifchannel_compare
);
38 int pim_ifchannel_compare(const struct pim_ifchannel
*ch1
,
39 const struct pim_ifchannel
*ch2
)
41 struct pim_interface
*pim_ifp1
;
42 struct pim_interface
*pim_ifp2
;
44 pim_ifp1
= ch1
->interface
->info
;
45 pim_ifp2
= ch2
->interface
->info
;
47 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
50 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
53 return pim_sgaddr_cmp(ch1
->sg
, ch2
->sg
);
57 * A (*,G) or a (*,*) is going away
58 * remove the parent pointer from
59 * those pointing at us
61 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
63 struct pim_ifchannel
*child
;
68 while (!list_isempty(ch
->sources
)) {
69 child
= listnode_head(ch
->sources
);
71 listnode_delete(ch
->sources
, child
);
76 * A (*,G) or a (*,*) is being created
77 * find all the children that would point
80 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
82 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
83 struct pim_ifchannel
*child
;
85 // Basic Sanity that we are not being silly
86 if (!pim_addr_is_any(ch
->sg
.src
) && !pim_addr_is_any(ch
->sg
.grp
))
89 if (pim_addr_is_any(ch
->sg
.src
) && pim_addr_is_any(ch
->sg
.grp
))
92 RB_FOREACH (child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
93 if (!pim_addr_is_any(ch
->sg
.grp
) &&
94 !pim_addr_cmp(child
->sg
.grp
, ch
->sg
.grp
) && (child
!= ch
)) {
96 listnode_add_sort(ch
->sources
, child
);
101 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
103 struct pim_interface
*pim_ifp
;
104 struct pim_upstream
*up
;
106 pim_ifp
= ch
->interface
->info
;
108 if (PIM_DEBUG_PIM_TRACE
)
109 zlog_debug("%s: ifchannel entry %s(%s) del start", __func__
,
110 ch
->sg_str
, ch
->interface
->name
);
112 if (PIM_I_am_DualActive(pim_ifp
)) {
115 "%s: if-chnanel-%s is deleted from a Dual active Interface",
116 __func__
, ch
->sg_str
);
117 /* Post Delete only if it is the last Dual-active Interface */
118 if (ch
->upstream
->dualactive_ifchannel_count
== 1) {
119 pim_mlag_up_local_del(pim_ifp
->pim
, ch
->upstream
);
120 PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
121 ch
->upstream
->flags
);
123 ch
->upstream
->dualactive_ifchannel_count
--;
126 if (ch
->upstream
->channel_oil
) {
127 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
128 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
129 mask
|= PIM_OIF_FLAG_PROTO_GM
;
132 * A S,G RPT channel can have an empty oil, we also
133 * need to take into account the fact that a ifchannel
134 * might have been suppressing a *,G ifchannel from
135 * being inherited. So let's figure out what
136 * needs to be done here
138 if (!pim_addr_is_any(ch
->sg
.src
) &&
139 pim_upstream_evaluate_join_desired_interface(
140 ch
->upstream
, ch
, ch
->parent
))
141 pim_channel_add_oif(ch
->upstream
->channel_oil
,
143 PIM_OIF_FLAG_PROTO_STAR
,
146 pim_channel_del_oif(ch
->upstream
->channel_oil
,
147 ch
->interface
, mask
, __func__
);
149 * Do we have any S,G's that are inheriting?
150 * Nuke from on high too.
152 if (ch
->upstream
->sources
) {
153 struct pim_upstream
*child
;
154 struct listnode
*up_node
;
156 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
158 pim_channel_del_inherited_oif(
166 * When this channel is removed
167 * we need to find all our children
168 * and make sure our pointers are fixed
170 pim_ifchannel_remove_children(ch
);
173 list_delete(&ch
->sources
);
175 listnode_delete(ch
->upstream
->ifchannels
, ch
);
179 /* upstream is common across ifchannels, check if upstream's
180 ifchannel list is empty before deleting upstream_del
181 ref count will take care of it.
183 if (ch
->upstream
->ref_count
> 0)
184 up
= pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __func__
);
187 if (PIM_DEBUG_PIM_TRACE
)
189 "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
190 __func__
, ch
->upstream
->ref_count
,
191 ch
->interface
->name
, ch
->sg_str
);
196 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
197 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
198 THREAD_OFF(ch
->t_ifassert_timer
);
201 listnode_delete(ch
->parent
->sources
, ch
);
205 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
207 if (PIM_DEBUG_PIM_TRACE
)
208 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__
,
209 ch
->sg_str
, ch
->interface
->name
);
211 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
214 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
217 void pim_ifchannel_delete_all(struct interface
*ifp
)
219 struct pim_interface
*pim_ifp
;
220 struct pim_ifchannel
*ch
;
226 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
227 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
229 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
230 pim_ifchannel_delete(ch
);
234 void delete_on_noinfo(struct pim_ifchannel
*ch
)
236 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
237 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
238 && ch
->t_ifjoin_expiry_timer
== NULL
)
239 pim_ifchannel_delete(ch
);
242 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
243 enum pim_ifjoin_state new_state
)
245 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
246 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
247 struct pim_ifchannel
*child_ch
;
249 if (PIM_DEBUG_PIM_EVENTS
)
251 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
252 ch
->interface
->name
, ch
->sg_str
,
253 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
254 pim_ifchannel_ifjoin_name(new_state
, 0));
257 if (old_state
== new_state
) {
258 if (PIM_DEBUG_PIM_EVENTS
) {
260 "%s called by %s: non-transition on state %d (%s)",
261 __func__
, caller
, new_state
,
262 pim_ifchannel_ifjoin_name(new_state
, 0));
267 ch
->ifjoin_state
= new_state
;
269 if (pim_addr_is_any(ch
->sg
.src
)) {
270 struct pim_upstream
*up
= ch
->upstream
;
271 struct pim_upstream
*child
;
272 struct listnode
*up_node
;
275 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
276 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
278 struct channel_oil
*c_oil
=
281 if (PIM_DEBUG_PIM_TRACE
)
283 "%s %s: Prune(S,G)=%s from %s",
291 * If the S,G has no if channel and the
293 * has output here then the *,G was
294 * supplying the implied
295 * if channel. So remove it.
297 if (oil_if_has(c_oil
,
298 pim_ifp
->mroute_vif_index
))
299 pim_channel_del_inherited_oif(
300 c_oil
, ch
->interface
,
304 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
305 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
307 if (PIM_DEBUG_PIM_TRACE
)
309 "%s %s: Join(S,G)=%s from %s",
314 /* check if the channel can be
315 * inherited into the SG's OIL
317 child_ch
= pim_ifchannel_find(
320 if (pim_upstream_eval_inherit_if(
321 child
, child_ch
, ch
)) {
325 PIM_OIF_FLAG_PROTO_STAR
,
327 pim_upstream_update_join_desired(
328 pim_ifp
->pim
, child
);
334 /* Transition to/from NOINFO ? */
335 if ((old_state
== PIM_IFJOIN_NOINFO
)
336 || (new_state
== PIM_IFJOIN_NOINFO
)) {
338 if (PIM_DEBUG_PIM_EVENTS
) {
339 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
340 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
342 ch
->sg_str
, ch
->interface
->name
);
346 Record uptime of state transition to/from NOINFO
348 ch
->ifjoin_creation
= pim_time_monotonic_sec();
350 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
351 pim_ifchannel_update_could_assert(ch
);
352 pim_ifchannel_update_assert_tracking_desired(ch
);
356 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
359 switch (ifjoin_state
) {
360 case PIM_IFJOIN_NOINFO
:
361 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
365 case PIM_IFJOIN_JOIN
:
367 case PIM_IFJOIN_PRUNE
:
368 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
372 case PIM_IFJOIN_PRUNE_PENDING
:
373 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
377 case PIM_IFJOIN_PRUNE_TMP
:
378 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
382 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
383 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
389 return "ifjoin_bad_state";
392 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
394 switch (ifassert_state
) {
395 case PIM_IFASSERT_NOINFO
:
397 case PIM_IFASSERT_I_AM_WINNER
:
399 case PIM_IFASSERT_I_AM_LOSER
:
403 return "ifassert_bad_state";
407 RFC 4601: 4.6.5. Assert State Macros
409 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
410 defaults to Infinity when in the NoInfo state.
412 void reset_ifassert_state(struct pim_ifchannel
*ch
)
414 THREAD_OFF(ch
->t_ifassert_timer
);
416 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, PIMADDR_ANY
,
417 router
->infinite_assert_metric
);
420 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
, pim_sgaddr
*sg
)
422 struct pim_interface
*pim_ifp
;
423 struct pim_ifchannel
*ch
;
424 struct pim_ifchannel lookup
;
429 zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
430 __func__
, sg
, ifp
->name
);
435 lookup
.interface
= ifp
;
436 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
441 static void ifmembership_set(struct pim_ifchannel
*ch
,
442 enum pim_ifmembership membership
)
444 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
446 if (ch
->local_ifmembership
== membership
)
449 if (PIM_DEBUG_PIM_EVENTS
) {
450 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
451 __func__
, ch
->sg_str
,
452 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
454 ch
->interface
->name
);
457 ch
->local_ifmembership
= membership
;
459 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
460 pim_ifchannel_update_could_assert(ch
);
461 pim_ifchannel_update_assert_tracking_desired(ch
);
465 void pim_ifchannel_membership_clear(struct interface
*ifp
)
467 struct pim_interface
*pim_ifp
;
468 struct pim_ifchannel
*ch
;
473 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
474 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
477 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
479 struct pim_interface
*pim_ifp
;
480 struct pim_ifchannel
*ch
, *ch_tmp
;
485 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
486 delete_on_noinfo(ch
);
490 * For a given Interface, if we are given a S,G
491 * Find the *,G (If we have it).
492 * If we are passed a *,G, find the *,* ifchannel
495 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
497 pim_sgaddr parent_sg
= ch
->sg
;
498 struct pim_ifchannel
*parent
= NULL
;
501 if (!pim_addr_is_any(parent_sg
.src
) &&
502 !pim_addr_is_any(parent_sg
.grp
)) {
503 parent_sg
.src
= PIMADDR_ANY
;
504 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
507 listnode_add(parent
->sources
, ch
);
514 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
, pim_sgaddr
*sg
,
515 uint8_t source_flags
, int up_flags
)
517 struct pim_interface
*pim_ifp
;
518 struct pim_ifchannel
*ch
;
519 struct pim_upstream
*up
;
521 ch
= pim_ifchannel_find(ifp
, sg
);
523 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
524 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
526 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
527 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
529 ch
->upstream
->flags
|= up_flags
;
536 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
539 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
540 && !(source_flags
& PIM_ENCODE_WC_BIT
))
541 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
545 snprintfrr(ch
->sg_str
, sizeof(ch
->sg_str
), "%pSG", sg
);
546 ch
->parent
= pim_ifchannel_find_parent(ch
);
547 if (pim_addr_is_any(ch
->sg
.src
)) {
548 ch
->sources
= list_new();
550 (int (*)(void *, void *))pim_ifchannel_compare
;
554 pim_ifchannel_find_new_children(ch
);
555 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
557 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
558 ch
->t_ifjoin_expiry_timer
= NULL
;
559 ch
->t_ifjoin_prune_pending_timer
= NULL
;
560 ch
->ifjoin_creation
= 0;
562 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
564 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
, __func__
, ch
);
568 listnode_add_sort(up
->ifchannels
, ch
);
570 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
571 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
573 ch
->ifassert_winner
= PIMADDR_ANY
;
576 ch
->t_ifassert_timer
= NULL
;
577 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
578 reset_ifassert_state(ch
);
579 if (pim_macro_ch_could_assert_eval(ch
))
580 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
582 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
584 if (pim_macro_assert_tracking_desired_eval(ch
))
585 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
587 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
590 * advertise MLAG Data to MLAG peer
592 if (PIM_I_am_DualActive(pim_ifp
)) {
593 up
->dualactive_ifchannel_count
++;
594 /* Sync once for upstream */
595 if (up
->dualactive_ifchannel_count
== 1) {
596 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up
->flags
);
597 pim_mlag_up_local_add(pim_ifp
->pim
, up
);
601 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
602 __func__
, up
->sg_str
,
603 up
->dualactive_ifchannel_count
, up
->flags
);
606 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
607 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
609 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
610 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
612 if (PIM_DEBUG_PIM_TRACE
)
613 zlog_debug("%s: ifchannel %s(%s) is created ", __func__
,
614 ch
->sg_str
, ch
->interface
->name
);
619 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
621 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
622 pim_forward_stop(ch
);
624 PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch
->upstream
->flags
);
626 PIM_IF_FLAG_UNSET_PROTO_PIM(ch
->flags
);
628 delete_on_noinfo(ch
);
631 static void on_ifjoin_expiry_timer(struct thread
*t
)
633 struct pim_ifchannel
*ch
;
637 if (PIM_DEBUG_PIM_TRACE
)
638 zlog_debug("%s: ifchannel %s expiry timer", __func__
,
641 ifjoin_to_noinfo(ch
);
642 /* ch may have been deleted */
645 static void on_ifjoin_prune_pending_timer(struct thread
*t
)
647 struct pim_ifchannel
*ch
;
648 int send_prune_echo
; /* boolean */
649 struct interface
*ifp
;
650 struct pim_interface
*pim_ifp
;
654 if (PIM_DEBUG_PIM_TRACE
)
655 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
657 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
659 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
662 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
663 /* Send PruneEcho(S,G) ? */
665 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
667 if (send_prune_echo
) {
670 rpf
.source_nexthop
.interface
= ifp
;
671 rpf
.rpf_addr
= pim_ifp
->primary_address
;
672 pim_jp_agg_single_upstream_send(
673 &rpf
, ch
->upstream
, 0);
676 ifjoin_to_noinfo(ch
);
678 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
679 * message on RP path upon prune timer expiry.
681 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
682 struct pim_upstream
*parent
=
683 ch
->upstream
->parent
;
685 pim_upstream_update_join_desired(pim_ifp
->pim
,
688 pim_jp_agg_single_upstream_send(&parent
->rpf
,
691 * SGRpt prune pending expiry has to install
692 * SG entry with empty olist to drop the SG
693 * traffic incase no other intf exists.
694 * On that scenario, SG entry wouldn't have
695 * got installed until Prune pending timer
696 * expired. So install now.
699 ch
->upstream
->channel_oil
, ifp
,
700 PIM_OIF_FLAG_PROTO_STAR
, __func__
);
701 pim_channel_del_oif(ch
->upstream
->channel_oil
, ifp
,
702 PIM_OIF_FLAG_PROTO_PIM
, __func__
);
703 if (!ch
->upstream
->channel_oil
->installed
)
704 pim_upstream_mroute_add(
705 ch
->upstream
->channel_oil
,
708 /* from here ch may have been deleted */
712 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
713 pim_addr upstream
, pim_sgaddr
*sg
,
714 uint8_t source_flags
, int holdtime
)
716 struct pim_upstream
*up
;
717 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
720 /* Upstream (S,G) in Joined state ? */
721 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
724 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
727 /* Upstream (S,G) in Joined state */
729 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
730 /* RPF'(S,G) not found */
731 zlog_warn("%s %s: RPF'%s not found", __FILE__
, __func__
,
736 rpf_addr
= up
->rpf
.rpf_addr
;
738 /* upstream directed to RPF'(S,G) ? */
739 if (pim_addr_cmp(upstream
, rpf_addr
)) {
741 "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
742 __FILE__
, __func__
, up
->sg_str
, &upstream
, &rpf_addr
,
746 /* upstream directed to RPF'(S,G) */
749 /* Join(S,G) to RPF'(S,G) */
750 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
, holdtime
);
754 /* Prune to RPF'(S,G) */
756 if (source_flags
& PIM_RPT_BIT_MASK
) {
757 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
758 /* Prune(*,G) to RPF'(S,G) */
759 pim_upstream_join_timer_decrease_to_t_override(
764 /* Prune(S,G,rpt) to RPF'(S,G) */
765 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
770 /* Prune(S,G) to RPF'(S,G) */
771 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
774 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
775 pim_addr upstream
, pim_sgaddr
*sg
,
776 uint8_t source_flags
, uint16_t holdtime
)
778 struct pim_interface
*recv_pim_ifp
;
779 int is_local
; /* boolean */
781 recv_pim_ifp
= recv_ifp
->info
;
782 assert(recv_pim_ifp
);
784 is_local
= !pim_addr_cmp(upstream
, recv_pim_ifp
->primary_address
);
789 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
791 "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
792 __func__
, is_join
? "join" : "prune", sg
, &upstream
,
796 * Since recv upstream addr was not directed to our primary
797 * address, check if we should react to it in any way.
799 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
802 return 1; /* non-local */
805 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel
*ch
,
806 struct pim_interface
*pim_ifp
)
808 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
809 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
810 /* check if the interface qualifies as an immediate
813 if (pim_upstream_evaluate_join_desired_interface(
816 pim_channel_add_oif(ch
->upstream
->channel_oil
,
818 PIM_OIF_FLAG_PROTO_PIM
,
820 pim_upstream_update_join_desired(pim_ifp
->pim
,
826 void pim_ifchannel_join_add(struct interface
*ifp
, pim_addr neigh_addr
,
827 pim_addr upstream
, pim_sgaddr
*sg
,
828 uint8_t source_flags
, uint16_t holdtime
)
830 struct pim_interface
*pim_ifp
;
831 struct pim_ifchannel
*ch
;
833 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
838 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
839 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
842 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
844 Transitions from "I am Assert Loser" State
846 Receive Join(S,G) on Interface I
848 We receive a Join(S,G) that has the Upstream Neighbor Address
849 field set to my primary IP address on interface I. The action is
850 to transition to NoInfo state, delete this (S,G) assert state
851 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
854 Notice: The nonlocal_upstream() test above ensures the upstream
855 address of the join message is our primary address.
857 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
858 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
859 __func__
, ch
->sg_str
, &neigh_addr
, ifp
->name
);
861 assert_action_a5(ch
);
867 switch (ch
->ifjoin_state
) {
868 case PIM_IFJOIN_NOINFO
:
869 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
870 if (pim_macro_chisin_oiflist(ch
)) {
871 pim_upstream_inherited_olist(pim_ifp
->pim
,
873 pim_forward_start(ch
);
876 * If we are going to be a LHR, we need to note it
878 if (ch
->upstream
->parent
&&
879 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
880 ch
->upstream
->parent
->flags
))
881 && !(ch
->upstream
->flags
882 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
883 pim_upstream_ref(ch
->upstream
,
884 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
886 pim_upstream_keep_alive_timer_start(
887 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
890 case PIM_IFJOIN_JOIN
:
891 assert(!ch
->t_ifjoin_prune_pending_timer
);
894 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
896 previously received join message with holdtime=0xFFFF.
898 if (ch
->t_ifjoin_expiry_timer
) {
899 unsigned long remain
= thread_timer_remain_second(
900 ch
->t_ifjoin_expiry_timer
);
901 if (remain
> holdtime
) {
903 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
906 Transitions from Join State
908 The (S,G) downstream state machine on
909 interface I remains in
910 Join state, and the Expiry Timer (ET) is
912 maximum of its current value and the HoldTime
914 triggering Join/Prune message.
916 Conclusion: Do not change the ET if the
918 higher than the received join holdtime.
923 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
925 case PIM_IFJOIN_PRUNE
:
926 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
927 pim_ifchannel_ifjoin_switch(__func__
, ch
,
929 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
930 delete_on_noinfo(ch
);
933 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
935 case PIM_IFJOIN_PRUNE_PENDING
:
937 * Transitions from Prune-Pending State (Receive Join)
938 * RFC 7761 Sec 4.5.2:
939 * The (S,G) downstream state machine on interface I
940 * transitions to the Join state. The Prune-Pending Timer is
941 * canceled (without triggering an expiry event). The
942 * Expiry Timer (ET) is restarted and is then set to the
943 * maximum of its current value and the HoldTime from the
944 * triggering Join/Prune message.
946 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
948 /* Check if SGRpt join Received */
949 if ((source_flags
& PIM_ENCODE_RPT_BIT
) &&
950 !pim_addr_is_any(sg
->src
)) {
952 * Transitions from Prune-Pending State (Rcv SGRpt Join)
953 * RFC 7761 Sec 4.5.3:
954 * The (S,G,rpt) downstream state machine on interface
955 * I transitions to the NoInfo state.The ET and PPT are
958 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
959 pim_ifchannel_ifjoin_switch(__func__
, ch
,
964 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
966 if (ch
->t_ifjoin_expiry_timer
) {
967 unsigned long remain
= thread_timer_remain_second(
968 ch
->t_ifjoin_expiry_timer
);
970 if (remain
> holdtime
)
973 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
976 case PIM_IFJOIN_PRUNE_TMP
:
978 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
982 if (holdtime
!= 0xFFFF) {
983 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
984 holdtime
, &ch
->t_ifjoin_expiry_timer
);
988 void pim_ifchannel_prune(struct interface
*ifp
, pim_addr upstream
,
989 pim_sgaddr
*sg
, uint8_t source_flags
,
992 struct pim_ifchannel
*ch
;
993 struct pim_interface
*pim_ifp
;
994 int jp_override_interval_msec
;
996 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
1001 ch
= pim_ifchannel_find(ifp
, sg
);
1002 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
1003 if (PIM_DEBUG_PIM_TRACE
)
1004 zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1005 __func__
, ifp
->name
, sg
,
1010 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
1011 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
1013 pim_ifp
= ifp
->info
;
1015 switch (ch
->ifjoin_state
) {
1016 case PIM_IFJOIN_NOINFO
:
1017 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1018 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
1019 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
1021 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1022 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1023 jp_override_interval_msec
=
1024 pim_if_jp_override_interval_msec(ifp
);
1026 jp_override_interval_msec
=
1027 0; /* schedule to expire immediately */
1028 /* If we called ifjoin_prune() directly instead, care
1030 be taken not to use "ch" afterwards since it would be
1033 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1034 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1035 thread_add_timer_msec(
1036 router
->master
, on_ifjoin_prune_pending_timer
,
1037 ch
, jp_override_interval_msec
,
1038 &ch
->t_ifjoin_prune_pending_timer
);
1039 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1041 &ch
->t_ifjoin_expiry_timer
);
1042 pim_upstream_update_join_desired(pim_ifp
->pim
,
1046 case PIM_IFJOIN_PRUNE_PENDING
:
1049 case PIM_IFJOIN_JOIN
:
1051 * The (S,G) downstream state machine on interface I
1052 * transitions to the Prune-Pending state. The
1053 * Prune-Pending Timer is started. It is set to the
1054 * J/P_Override_Interval(I) if the router has more than one
1055 * neighbor on that interface; otherwise, it is set to zero,
1056 * causing it to expire immediately.
1059 pim_ifchannel_ifjoin_switch(__func__
, ch
,
1060 PIM_IFJOIN_PRUNE_PENDING
);
1062 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1063 jp_override_interval_msec
=
1064 pim_if_jp_override_interval_msec(ifp
);
1066 jp_override_interval_msec
=
1067 0; /* schedule to expire immediately */
1068 /* If we called ifjoin_prune() directly instead, care should
1069 be taken not to use "ch" afterwards since it would be
1071 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1072 thread_add_timer_msec(router
->master
,
1073 on_ifjoin_prune_pending_timer
, ch
,
1074 jp_override_interval_msec
,
1075 &ch
->t_ifjoin_prune_pending_timer
);
1077 case PIM_IFJOIN_PRUNE
:
1078 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1079 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1081 * While in Prune State, Receive SGRpt Prune.
1082 * RFC 7761 Sec 4.5.3:
1083 * The (S,G,rpt) downstream state machine on interface I
1084 * remains in Prune state. The Expiry Timer (ET) is
1085 * restarted and is then set to the maximum of its
1086 * current value and the HoldTime from the triggering
1087 * Join/Prune message.
1089 if (ch
->t_ifjoin_expiry_timer
) {
1090 unsigned long rem
= thread_timer_remain_second(
1091 ch
->t_ifjoin_expiry_timer
);
1095 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1098 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1100 &ch
->t_ifjoin_expiry_timer
);
1103 case PIM_IFJOIN_PRUNE_TMP
:
1104 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1105 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1106 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1107 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1109 &ch
->t_ifjoin_expiry_timer
);
1112 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1113 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1114 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1115 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1116 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1118 &ch
->t_ifjoin_expiry_timer
);
1124 int pim_ifchannel_local_membership_add(struct interface
*ifp
, pim_sgaddr
*sg
,
1127 struct pim_ifchannel
*ch
, *starch
;
1128 struct pim_interface
*pim_ifp
;
1129 struct pim_instance
*pim
;
1132 /* PIM enabled on interface? */
1133 pim_ifp
= ifp
->info
;
1135 if (PIM_DEBUG_EVENTS
)
1136 zlog_debug("%s:%pSG Expected pim interface setup for %s",
1137 __func__
, sg
, ifp
->name
);
1141 if (!pim_ifp
->pim_enable
) {
1142 if (PIM_DEBUG_EVENTS
)
1143 zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1144 __func__
, sg
, ifp
->name
);
1150 /* skip (*,G) ch creation if G is of type SSM */
1151 if (pim_addr_is_any(sg
->src
)) {
1152 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1153 if (PIM_DEBUG_PIM_EVENTS
)
1154 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1160 /* vxlan term mroutes use ipmr-lo as local member to
1161 * pull down multicast vxlan tunnel traffic
1163 up_flags
= is_vxlan
? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
:
1164 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
;
1165 ch
= pim_ifchannel_add(ifp
, sg
, 0, up_flags
);
1167 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1169 if (pim_addr_is_any(sg
->src
)) {
1170 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1171 struct pim_upstream
*child
;
1172 struct listnode
*up_node
;
1176 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1177 if (PIM_DEBUG_EVENTS
)
1178 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1179 __FILE__
, __func__
, child
->sg_str
,
1180 ifp
->name
, up
->sg_str
);
1182 if (!child
->rpf
.source_nexthop
.interface
) {
1183 /* when iif unknown, do not inherit */
1184 if (PIM_DEBUG_EVENTS
)
1186 "Skipped (S,G)=%s(%s) from %s: no iif",
1187 child
->sg_str
, ifp
->name
,
1192 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1193 if (pim_upstream_evaluate_join_desired_interface(
1194 child
, ch
, starch
)) {
1195 pim_channel_add_oif(child
->channel_oil
, ifp
,
1196 PIM_OIF_FLAG_PROTO_STAR
,
1198 pim_upstream_update_join_desired(pim
, child
);
1202 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1203 if (pim
->spt
.plist
) {
1204 struct prefix_list
*plist
= prefix_list_lookup(
1205 AFI_IP
, pim
->spt
.plist
);
1208 pim_addr_to_prefix(&g
, up
->sg
.grp
);
1209 if (prefix_list_apply_ext(plist
, NULL
, &g
,
1212 pim_channel_add_oif(
1213 up
->channel_oil
, pim
->regiface
,
1214 PIM_OIF_FLAG_PROTO_GM
,
1219 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1220 PIM_OIF_FLAG_PROTO_GM
, __func__
);
1226 void pim_ifchannel_local_membership_del(struct interface
*ifp
, pim_sgaddr
*sg
)
1228 struct pim_ifchannel
*starch
, *ch
, *orig
;
1229 struct pim_interface
*pim_ifp
;
1231 /* PIM enabled on interface? */
1232 pim_ifp
= ifp
->info
;
1235 if (!pim_ifp
->pim_enable
)
1238 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1241 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1243 if (pim_addr_is_any(sg
->src
)) {
1244 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1245 struct pim_upstream
*child
;
1246 struct listnode
*up_node
, *up_nnode
;
1250 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1251 struct channel_oil
*c_oil
= child
->channel_oil
;
1252 struct pim_ifchannel
*chchannel
=
1253 pim_ifchannel_find(ifp
, &child
->sg
);
1255 pim_ifp
= ifp
->info
;
1257 if (PIM_DEBUG_EVENTS
)
1258 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1259 __FILE__
, __func__
, up
->sg_str
,
1260 ifp
->name
, child
->sg_str
);
1262 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1264 * If the S,G has no if channel and the c_oil still
1265 * has output here then the *,G was supplying the
1267 * if channel. So remove it.
1269 if (!pim_upstream_evaluate_join_desired_interface(
1270 child
, ch
, starch
) ||
1272 oil_if_has(c_oil
, pim_ifp
->mroute_vif_index
))) {
1273 pim_channel_del_inherited_oif(c_oil
, ifp
,
1277 /* Child node removal/ref count-- will happen as part of
1278 * parent' delete_no_info */
1282 /* Resettng the IGMP flags here */
1284 PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig
->upstream
->flags
);
1286 PIM_IF_FLAG_UNSET_PROTO_IGMP(orig
->flags
);
1288 delete_on_noinfo(orig
);
1291 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1293 int old_couldassert
=
1294 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1295 int new_couldassert
=
1296 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1298 if (new_couldassert
== old_couldassert
)
1301 if (PIM_DEBUG_PIM_EVENTS
)
1302 zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1303 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
,
1304 ch
->interface
->name
, old_couldassert
,
1307 if (new_couldassert
) {
1308 /* CouldAssert(S,G,I) switched from false to true */
1309 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1311 /* CouldAssert(S,G,I) switched from true to false */
1312 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1314 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1315 assert_action_a4(ch
);
1319 pim_ifchannel_update_my_assert_metric(ch
);
1323 my_assert_metric may be affected by:
1326 pim_ifp->primary_address
1327 rpf->source_nexthop.mrib_metric_preference;
1328 rpf->source_nexthop.mrib_route_metric;
1330 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1332 struct pim_assert_metric my_metric_new
=
1333 pim_macro_ch_my_assert_metric_eval(ch
);
1335 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1338 if (PIM_DEBUG_PIM_EVENTS
)
1340 "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1341 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1342 ch
->ifassert_my_metric
.rpt_bit_flag
,
1343 ch
->ifassert_my_metric
.metric_preference
,
1344 ch
->ifassert_my_metric
.route_metric
,
1345 &ch
->ifassert_my_metric
.ip_address
,
1346 my_metric_new
.rpt_bit_flag
,
1347 my_metric_new
.metric_preference
,
1348 my_metric_new
.route_metric
, &my_metric_new
.ip_address
);
1350 ch
->ifassert_my_metric
= my_metric_new
;
1352 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1353 &ch
->ifassert_winner_metric
)) {
1354 assert_action_a5(ch
);
1358 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1360 int old_atd
= PIM_FORCE_BOOLEAN(
1361 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1363 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1365 if (new_atd
== old_atd
)
1368 if (PIM_DEBUG_PIM_EVENTS
)
1370 "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1371 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1375 /* AssertTrackingDesired(S,G,I) switched from false to true */
1376 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1378 /* AssertTrackingDesired(S,G,I) switched from true to false */
1379 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1381 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1382 assert_action_a5(ch
);
1388 * If we have a new pim interface, check to
1389 * see if any of the pre-existing channels have
1390 * their upstream out that way and turn on forwarding
1391 * for that ifchannel then.
1393 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1395 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1396 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1397 struct interface
*ifp
;
1399 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1400 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1401 struct pim_ifchannel
*ch
;
1406 if (new_pim_ifp
== loop_pim_ifp
)
1409 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1410 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1411 struct pim_upstream
*up
= ch
->upstream
;
1412 if ((!up
->channel_oil
)
1413 && (up
->rpf
.source_nexthop
1414 .interface
== new_ifp
))
1415 pim_forward_start(ch
);
1422 * Downstream per-interface (S,G,rpt) state machine
1423 * states that we need to move (S,G,rpt) items
1424 * into different states at the start of the
1425 * reception of a *,G join as well, when
1426 * we get End of Message
1428 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1431 bool send_upstream_starg
= false;
1432 struct pim_ifchannel
*child
;
1433 struct listnode
*ch_node
, *nch_node
;
1434 struct pim_instance
*pim
=
1435 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1436 struct pim_upstream
*starup
= ch
->upstream
;
1438 if (PIM_DEBUG_PIM_TRACE
)
1440 "%s: %s %s eom: %d join %u", __func__
,
1441 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1442 ch
->sg_str
, eom
, join
);
1446 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1447 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1450 switch (child
->ifjoin_state
) {
1451 case PIM_IFJOIN_NOINFO
:
1452 case PIM_IFJOIN_JOIN
:
1454 case PIM_IFJOIN_PRUNE
:
1456 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1458 case PIM_IFJOIN_PRUNE_PENDING
:
1460 child
->ifjoin_state
=
1461 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1463 case PIM_IFJOIN_PRUNE_TMP
:
1464 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1468 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1469 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1470 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1472 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1473 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1475 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1476 (!pim_upstream_empty_inherited_olist(
1477 child
->upstream
))) {
1478 pim_channel_add_oif(
1479 child
->upstream
->channel_oil
,
1480 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
,
1482 pim_upstream_update_join_desired(pim
,
1485 send_upstream_starg
= true;
1487 delete_on_noinfo(child
);
1492 if (send_upstream_starg
)
1493 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);