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
);
542 ch
->upstream
->flags
|= up_flags
;
549 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
552 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
553 && !(source_flags
& PIM_ENCODE_WC_BIT
))
554 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
558 snprintfrr(ch
->sg_str
, sizeof(ch
->sg_str
), "%pSG", sg
);
559 ch
->parent
= pim_ifchannel_find_parent(ch
);
560 if (pim_addr_is_any(ch
->sg
.src
)) {
561 ch
->sources
= list_new();
563 (int (*)(void *, void *))pim_ifchannel_compare
;
567 pim_ifchannel_find_new_children(ch
);
568 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
570 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
571 ch
->t_ifjoin_expiry_timer
= NULL
;
572 ch
->t_ifjoin_prune_pending_timer
= NULL
;
573 ch
->ifjoin_creation
= 0;
575 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
577 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
, __func__
, ch
);
581 listnode_add_sort(up
->ifchannels
, ch
);
583 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
584 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
586 ch
->ifassert_winner
= PIMADDR_ANY
;
589 ch
->t_ifassert_timer
= NULL
;
590 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
591 reset_ifassert_state(ch
);
592 if (pim_macro_ch_could_assert_eval(ch
))
593 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
595 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
597 if (pim_macro_assert_tracking_desired_eval(ch
))
598 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
600 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
603 * advertise MLAG Data to MLAG peer
605 if (PIM_I_am_DualActive(pim_ifp
)) {
606 up
->dualactive_ifchannel_count
++;
607 /* Sync once for upstream */
608 if (up
->dualactive_ifchannel_count
== 1) {
609 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up
->flags
);
610 pim_mlag_up_local_add(pim_ifp
->pim
, up
);
614 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
615 __func__
, up
->sg_str
,
616 up
->dualactive_ifchannel_count
, up
->flags
);
619 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
620 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
622 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
623 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
625 if (PIM_DEBUG_PIM_TRACE
)
626 zlog_debug("%s: ifchannel %s(%s) is created ", __func__
,
627 ch
->sg_str
, ch
->interface
->name
);
632 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
634 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
635 pim_forward_stop(ch
);
637 PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch
->upstream
->flags
);
639 PIM_IF_FLAG_UNSET_PROTO_PIM(ch
->flags
);
641 delete_on_noinfo(ch
);
644 static void on_ifjoin_expiry_timer(struct thread
*t
)
646 struct pim_ifchannel
*ch
;
650 if (PIM_DEBUG_PIM_TRACE
)
651 zlog_debug("%s: ifchannel %s expiry timer", __func__
,
654 ifjoin_to_noinfo(ch
);
655 /* ch may have been deleted */
658 static void on_ifjoin_prune_pending_timer(struct thread
*t
)
660 struct pim_ifchannel
*ch
;
661 int send_prune_echo
; /* boolean */
662 struct interface
*ifp
;
663 struct pim_interface
*pim_ifp
;
667 if (PIM_DEBUG_PIM_TRACE
)
668 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
670 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
672 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
675 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
676 /* Send PruneEcho(S,G) ? */
678 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
680 if (send_prune_echo
) {
683 rpf
.source_nexthop
.interface
= ifp
;
684 rpf
.rpf_addr
= pim_ifp
->primary_address
;
685 pim_jp_agg_single_upstream_send(
686 &rpf
, ch
->upstream
, 0);
689 ifjoin_to_noinfo(ch
);
691 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
692 * message on RP path upon prune timer expiry.
694 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
695 struct pim_upstream
*parent
=
696 ch
->upstream
->parent
;
698 pim_upstream_update_join_desired(pim_ifp
->pim
,
701 pim_jp_agg_single_upstream_send(&parent
->rpf
,
704 * SGRpt prune pending expiry has to install
705 * SG entry with empty olist to drop the SG
706 * traffic incase no other intf exists.
707 * On that scenario, SG entry wouldn't have
708 * got installed until Prune pending timer
709 * expired. So install now.
712 ch
->upstream
->channel_oil
, ifp
,
713 PIM_OIF_FLAG_PROTO_STAR
, __func__
);
714 if (!ch
->upstream
->channel_oil
->installed
)
715 pim_upstream_mroute_add(
716 ch
->upstream
->channel_oil
,
719 /* from here ch may have been deleted */
723 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
724 pim_addr upstream
, pim_sgaddr
*sg
,
725 uint8_t source_flags
, int holdtime
)
727 struct pim_upstream
*up
;
728 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
731 /* Upstream (S,G) in Joined state ? */
732 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
735 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
738 /* Upstream (S,G) in Joined state */
740 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
741 /* RPF'(S,G) not found */
742 zlog_warn("%s %s: RPF'%s not found", __FILE__
, __func__
,
747 rpf_addr
= up
->rpf
.rpf_addr
;
749 /* upstream directed to RPF'(S,G) ? */
750 if (pim_addr_cmp(upstream
, rpf_addr
)) {
752 "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
753 __FILE__
, __func__
, up
->sg_str
, &upstream
, &rpf_addr
,
757 /* upstream directed to RPF'(S,G) */
760 /* Join(S,G) to RPF'(S,G) */
761 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
, holdtime
);
765 /* Prune to RPF'(S,G) */
767 if (source_flags
& PIM_RPT_BIT_MASK
) {
768 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
769 /* Prune(*,G) to RPF'(S,G) */
770 pim_upstream_join_timer_decrease_to_t_override(
775 /* Prune(S,G,rpt) to RPF'(S,G) */
776 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
781 /* Prune(S,G) to RPF'(S,G) */
782 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
785 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
786 pim_addr upstream
, pim_sgaddr
*sg
,
787 uint8_t source_flags
, uint16_t holdtime
)
789 struct pim_interface
*recv_pim_ifp
;
790 int is_local
; /* boolean */
792 recv_pim_ifp
= recv_ifp
->info
;
793 assert(recv_pim_ifp
);
795 is_local
= !pim_addr_cmp(upstream
, recv_pim_ifp
->primary_address
);
800 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
802 "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
803 __func__
, is_join
? "join" : "prune", sg
, &upstream
,
807 * Since recv upstream addr was not directed to our primary
808 * address, check if we should react to it in any way.
810 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
813 return 1; /* non-local */
816 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel
*ch
,
817 struct pim_interface
*pim_ifp
)
819 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
820 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
821 /* check if the interface qualifies as an immediate
824 if (pim_upstream_evaluate_join_desired_interface(
827 pim_channel_add_oif(ch
->upstream
->channel_oil
,
829 PIM_OIF_FLAG_PROTO_PIM
,
831 pim_upstream_update_join_desired(pim_ifp
->pim
,
837 void pim_ifchannel_join_add(struct interface
*ifp
, pim_addr neigh_addr
,
838 pim_addr upstream
, pim_sgaddr
*sg
,
839 uint8_t source_flags
, uint16_t holdtime
)
841 struct pim_interface
*pim_ifp
;
842 struct pim_ifchannel
*ch
;
844 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
849 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
850 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
853 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
855 Transitions from "I am Assert Loser" State
857 Receive Join(S,G) on Interface I
859 We receive a Join(S,G) that has the Upstream Neighbor Address
860 field set to my primary IP address on interface I. The action is
861 to transition to NoInfo state, delete this (S,G) assert state
862 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
865 Notice: The nonlocal_upstream() test above ensures the upstream
866 address of the join message is our primary address.
868 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
869 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
870 __func__
, ch
->sg_str
, &neigh_addr
, ifp
->name
);
872 assert_action_a5(ch
);
878 switch (ch
->ifjoin_state
) {
879 case PIM_IFJOIN_NOINFO
:
880 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
881 if (pim_macro_chisin_oiflist(ch
)) {
882 pim_upstream_inherited_olist(pim_ifp
->pim
,
884 pim_forward_start(ch
);
887 * If we are going to be a LHR, we need to note it
889 if (ch
->upstream
->parent
&&
890 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
891 ch
->upstream
->parent
->flags
))
892 && !(ch
->upstream
->flags
893 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
894 pim_upstream_ref(ch
->upstream
,
895 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
897 pim_upstream_keep_alive_timer_start(
898 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
901 case PIM_IFJOIN_JOIN
:
902 assert(!ch
->t_ifjoin_prune_pending_timer
);
905 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
907 previously received join message with holdtime=0xFFFF.
909 if (ch
->t_ifjoin_expiry_timer
) {
910 unsigned long remain
= thread_timer_remain_second(
911 ch
->t_ifjoin_expiry_timer
);
912 if (remain
> holdtime
) {
914 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
917 Transitions from Join State
919 The (S,G) downstream state machine on
920 interface I remains in
921 Join state, and the Expiry Timer (ET) is
923 maximum of its current value and the HoldTime
925 triggering Join/Prune message.
927 Conclusion: Do not change the ET if the
929 higher than the received join holdtime.
934 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
936 case PIM_IFJOIN_PRUNE
:
937 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
938 pim_ifchannel_ifjoin_switch(__func__
, ch
,
940 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
941 delete_on_noinfo(ch
);
944 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
946 case PIM_IFJOIN_PRUNE_PENDING
:
948 * Transitions from Prune-Pending State (Receive Join)
949 * RFC 7761 Sec 4.5.2:
950 * The (S,G) downstream state machine on interface I
951 * transitions to the Join state. The Prune-Pending Timer is
952 * canceled (without triggering an expiry event). The
953 * Expiry Timer (ET) is restarted and is then set to the
954 * maximum of its current value and the HoldTime from the
955 * triggering Join/Prune message.
957 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
959 /* Check if SGRpt join Received */
960 if ((source_flags
& PIM_ENCODE_RPT_BIT
) &&
961 !pim_addr_is_any(sg
->src
)) {
963 * Transitions from Prune-Pending State (Rcv SGRpt Join)
964 * RFC 7761 Sec 4.5.3:
965 * The (S,G,rpt) downstream state machine on interface
966 * I transitions to the NoInfo state.The ET and PPT are
969 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
970 pim_ifchannel_ifjoin_switch(__func__
, ch
,
975 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
977 if (ch
->t_ifjoin_expiry_timer
) {
978 unsigned long remain
= thread_timer_remain_second(
979 ch
->t_ifjoin_expiry_timer
);
981 if (remain
> holdtime
)
984 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
987 case PIM_IFJOIN_PRUNE_TMP
:
989 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
993 if (holdtime
!= 0xFFFF) {
994 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
995 holdtime
, &ch
->t_ifjoin_expiry_timer
);
999 void pim_ifchannel_prune(struct interface
*ifp
, pim_addr upstream
,
1000 pim_sgaddr
*sg
, uint8_t source_flags
,
1003 struct pim_ifchannel
*ch
;
1004 struct pim_interface
*pim_ifp
;
1005 int jp_override_interval_msec
;
1007 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
1012 ch
= pim_ifchannel_find(ifp
, sg
);
1013 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
1014 if (PIM_DEBUG_PIM_TRACE
)
1015 zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1016 __func__
, ifp
->name
, sg
,
1021 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
1022 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
1024 pim_ifp
= ifp
->info
;
1026 switch (ch
->ifjoin_state
) {
1027 case PIM_IFJOIN_NOINFO
:
1028 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1029 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
1030 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
1032 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1033 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1034 jp_override_interval_msec
=
1035 pim_if_jp_override_interval_msec(ifp
);
1037 jp_override_interval_msec
=
1038 0; /* schedule to expire immediately */
1039 /* If we called ifjoin_prune() directly instead, care
1041 be taken not to use "ch" afterwards since it would be
1044 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1045 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1046 thread_add_timer_msec(
1047 router
->master
, on_ifjoin_prune_pending_timer
,
1048 ch
, jp_override_interval_msec
,
1049 &ch
->t_ifjoin_prune_pending_timer
);
1050 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1052 &ch
->t_ifjoin_expiry_timer
);
1053 pim_upstream_update_join_desired(pim_ifp
->pim
,
1057 case PIM_IFJOIN_PRUNE_PENDING
:
1060 case PIM_IFJOIN_JOIN
:
1062 * The (S,G) downstream state machine on interface I
1063 * transitions to the Prune-Pending state. The
1064 * Prune-Pending Timer is started. It is set to the
1065 * J/P_Override_Interval(I) if the router has more than one
1066 * neighbor on that interface; otherwise, it is set to zero,
1067 * causing it to expire immediately.
1070 pim_ifchannel_ifjoin_switch(__func__
, ch
,
1071 PIM_IFJOIN_PRUNE_PENDING
);
1073 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1074 jp_override_interval_msec
=
1075 pim_if_jp_override_interval_msec(ifp
);
1077 jp_override_interval_msec
=
1078 0; /* schedule to expire immediately */
1079 /* If we called ifjoin_prune() directly instead, care should
1080 be taken not to use "ch" afterwards since it would be
1082 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1083 thread_add_timer_msec(router
->master
,
1084 on_ifjoin_prune_pending_timer
, ch
,
1085 jp_override_interval_msec
,
1086 &ch
->t_ifjoin_prune_pending_timer
);
1088 case PIM_IFJOIN_PRUNE
:
1089 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1090 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1092 * While in Prune State, Receive SGRpt Prune.
1093 * RFC 7761 Sec 4.5.3:
1094 * The (S,G,rpt) downstream state machine on interface I
1095 * remains in Prune state. The Expiry Timer (ET) is
1096 * restarted and is then set to the maximum of its
1097 * current value and the HoldTime from the triggering
1098 * Join/Prune message.
1100 if (ch
->t_ifjoin_expiry_timer
) {
1101 unsigned long rem
= thread_timer_remain_second(
1102 ch
->t_ifjoin_expiry_timer
);
1106 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1109 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1111 &ch
->t_ifjoin_expiry_timer
);
1114 case PIM_IFJOIN_PRUNE_TMP
:
1115 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1116 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1117 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1118 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1120 &ch
->t_ifjoin_expiry_timer
);
1123 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1124 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1125 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1126 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1127 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1129 &ch
->t_ifjoin_expiry_timer
);
1135 int pim_ifchannel_local_membership_add(struct interface
*ifp
, pim_sgaddr
*sg
,
1138 struct pim_ifchannel
*ch
, *starch
;
1139 struct pim_interface
*pim_ifp
;
1140 struct pim_instance
*pim
;
1143 /* PIM enabled on interface? */
1144 pim_ifp
= ifp
->info
;
1146 if (PIM_DEBUG_EVENTS
)
1147 zlog_debug("%s:%pSG Expected pim interface setup for %s",
1148 __func__
, sg
, ifp
->name
);
1152 if (!pim_ifp
->pim_enable
) {
1153 if (PIM_DEBUG_EVENTS
)
1154 zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1155 __func__
, sg
, ifp
->name
);
1161 /* skip (*,G) ch creation if G is of type SSM */
1162 if (pim_addr_is_any(sg
->src
)) {
1163 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1164 if (PIM_DEBUG_PIM_EVENTS
)
1165 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1171 /* vxlan term mroutes use ipmr-lo as local member to
1172 * pull down multicast vxlan tunnel traffic
1174 up_flags
= is_vxlan
? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
:
1175 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
;
1176 ch
= pim_ifchannel_add(ifp
, sg
, 0, up_flags
);
1178 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1180 if (pim_addr_is_any(sg
->src
)) {
1181 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1182 struct pim_upstream
*child
;
1183 struct listnode
*up_node
;
1187 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1188 if (PIM_DEBUG_EVENTS
)
1189 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1190 __FILE__
, __func__
, child
->sg_str
,
1191 ifp
->name
, up
->sg_str
);
1193 if (!child
->rpf
.source_nexthop
.interface
) {
1194 /* when iif unknown, do not inherit */
1195 if (PIM_DEBUG_EVENTS
)
1197 "Skipped (S,G)=%s(%s) from %s: no iif",
1198 child
->sg_str
, ifp
->name
,
1203 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1204 if (pim_upstream_evaluate_join_desired_interface(
1205 child
, ch
, starch
)) {
1206 pim_channel_add_oif(child
->channel_oil
, ifp
,
1207 PIM_OIF_FLAG_PROTO_STAR
,
1209 pim_upstream_update_join_desired(pim
, child
);
1213 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1214 if (pim
->spt
.plist
) {
1215 struct prefix_list
*plist
= prefix_list_lookup(
1216 AFI_IP
, pim
->spt
.plist
);
1219 pim_addr_to_prefix(&g
, up
->sg
.grp
);
1220 if (prefix_list_apply(plist
, &g
)
1222 pim_channel_add_oif(
1223 up
->channel_oil
, pim
->regiface
,
1224 PIM_OIF_FLAG_PROTO_GM
,
1229 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1230 PIM_OIF_FLAG_PROTO_GM
, __func__
);
1236 void pim_ifchannel_local_membership_del(struct interface
*ifp
, pim_sgaddr
*sg
)
1238 struct pim_ifchannel
*starch
, *ch
, *orig
;
1239 struct pim_interface
*pim_ifp
;
1241 /* PIM enabled on interface? */
1242 pim_ifp
= ifp
->info
;
1245 if (!pim_ifp
->pim_enable
)
1248 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1251 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1253 if (pim_addr_is_any(sg
->src
)) {
1254 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1255 struct pim_upstream
*child
;
1256 struct listnode
*up_node
, *up_nnode
;
1260 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1261 struct channel_oil
*c_oil
= child
->channel_oil
;
1262 struct pim_ifchannel
*chchannel
=
1263 pim_ifchannel_find(ifp
, &child
->sg
);
1265 pim_ifp
= ifp
->info
;
1267 if (PIM_DEBUG_EVENTS
)
1268 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1269 __FILE__
, __func__
, up
->sg_str
,
1270 ifp
->name
, child
->sg_str
);
1272 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1274 * If the S,G has no if channel and the c_oil still
1275 * has output here then the *,G was supplying the
1277 * if channel. So remove it.
1279 if (!pim_upstream_evaluate_join_desired_interface(
1280 child
, ch
, starch
) ||
1282 oil_if_has(c_oil
, pim_ifp
->mroute_vif_index
))) {
1283 pim_channel_del_inherited_oif(c_oil
, ifp
,
1287 /* Child node removal/ref count-- will happen as part of
1288 * parent' delete_no_info */
1292 /* Resettng the IGMP flags here */
1294 PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig
->upstream
->flags
);
1296 PIM_IF_FLAG_UNSET_PROTO_IGMP(orig
->flags
);
1298 delete_on_noinfo(orig
);
1301 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1303 int old_couldassert
=
1304 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1305 int new_couldassert
=
1306 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1308 if (new_couldassert
== old_couldassert
)
1311 if (PIM_DEBUG_PIM_EVENTS
)
1312 zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1313 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
,
1314 ch
->interface
->name
, old_couldassert
,
1317 if (new_couldassert
) {
1318 /* CouldAssert(S,G,I) switched from false to true */
1319 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1321 /* CouldAssert(S,G,I) switched from true to false */
1322 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1324 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1325 assert_action_a4(ch
);
1329 pim_ifchannel_update_my_assert_metric(ch
);
1333 my_assert_metric may be affected by:
1336 pim_ifp->primary_address
1337 rpf->source_nexthop.mrib_metric_preference;
1338 rpf->source_nexthop.mrib_route_metric;
1340 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1342 struct pim_assert_metric my_metric_new
=
1343 pim_macro_ch_my_assert_metric_eval(ch
);
1345 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1348 if (PIM_DEBUG_PIM_EVENTS
)
1350 "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1351 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1352 ch
->ifassert_my_metric
.rpt_bit_flag
,
1353 ch
->ifassert_my_metric
.metric_preference
,
1354 ch
->ifassert_my_metric
.route_metric
,
1355 &ch
->ifassert_my_metric
.ip_address
,
1356 my_metric_new
.rpt_bit_flag
,
1357 my_metric_new
.metric_preference
,
1358 my_metric_new
.route_metric
, &my_metric_new
.ip_address
);
1360 ch
->ifassert_my_metric
= my_metric_new
;
1362 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1363 &ch
->ifassert_winner_metric
)) {
1364 assert_action_a5(ch
);
1368 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1370 int old_atd
= PIM_FORCE_BOOLEAN(
1371 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1373 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1375 if (new_atd
== old_atd
)
1378 if (PIM_DEBUG_PIM_EVENTS
)
1380 "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1381 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1385 /* AssertTrackingDesired(S,G,I) switched from false to true */
1386 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1388 /* AssertTrackingDesired(S,G,I) switched from true to false */
1389 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1391 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1392 assert_action_a5(ch
);
1398 * If we have a new pim interface, check to
1399 * see if any of the pre-existing channels have
1400 * their upstream out that way and turn on forwarding
1401 * for that ifchannel then.
1403 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1405 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1406 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1407 struct interface
*ifp
;
1409 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1410 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1411 struct pim_ifchannel
*ch
;
1416 if (new_pim_ifp
== loop_pim_ifp
)
1419 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1420 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1421 struct pim_upstream
*up
= ch
->upstream
;
1422 if ((!up
->channel_oil
)
1423 && (up
->rpf
.source_nexthop
1424 .interface
== new_ifp
))
1425 pim_forward_start(ch
);
1432 * Downstream per-interface (S,G,rpt) state machine
1433 * states that we need to move (S,G,rpt) items
1434 * into different states at the start of the
1435 * reception of a *,G join as well, when
1436 * we get End of Message
1438 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1441 bool send_upstream_starg
= false;
1442 struct pim_ifchannel
*child
;
1443 struct listnode
*ch_node
, *nch_node
;
1444 struct pim_instance
*pim
=
1445 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1446 struct pim_upstream
*starup
= ch
->upstream
;
1448 if (PIM_DEBUG_PIM_TRACE
)
1450 "%s: %s %s eom: %d join %u", __func__
,
1451 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1452 ch
->sg_str
, eom
, join
);
1456 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1457 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1460 switch (child
->ifjoin_state
) {
1461 case PIM_IFJOIN_NOINFO
:
1462 case PIM_IFJOIN_JOIN
:
1464 case PIM_IFJOIN_PRUNE
:
1466 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1468 case PIM_IFJOIN_PRUNE_PENDING
:
1470 child
->ifjoin_state
=
1471 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1473 case PIM_IFJOIN_PRUNE_TMP
:
1474 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1478 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1479 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1480 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1482 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1483 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1485 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1486 (!pim_upstream_empty_inherited_olist(
1487 child
->upstream
))) {
1488 pim_channel_add_oif(
1489 child
->upstream
->channel_oil
,
1490 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
,
1492 pim_upstream_update_join_desired(pim
,
1495 send_upstream_starg
= true;
1497 delete_on_noinfo(child
);
1502 if (send_upstream_starg
)
1503 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);