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
33 #include "pim_iface.h"
34 #include "pim_ifchannel.h"
35 #include "pim_zebra.h"
41 #include "pim_macro.h"
43 #include "pim_upstream.h"
48 RB_GENERATE(pim_ifchannel_rb
, pim_ifchannel
, pim_ifp_rb
, pim_ifchannel_compare
);
50 int pim_ifchannel_compare(const struct pim_ifchannel
*ch1
,
51 const struct pim_ifchannel
*ch2
)
53 struct pim_interface
*pim_ifp1
;
54 struct pim_interface
*pim_ifp2
;
56 pim_ifp1
= ch1
->interface
->info
;
57 pim_ifp2
= ch2
->interface
->info
;
59 if (pim_ifp1
->mroute_vif_index
< pim_ifp2
->mroute_vif_index
)
62 if (pim_ifp1
->mroute_vif_index
> pim_ifp2
->mroute_vif_index
)
65 return pim_sgaddr_cmp(ch1
->sg
, ch2
->sg
);
69 * A (*,G) or a (*,*) is going away
70 * remove the parent pointer from
71 * those pointing at us
73 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
75 struct pim_ifchannel
*child
;
80 while (!list_isempty(ch
->sources
)) {
81 child
= listnode_head(ch
->sources
);
83 listnode_delete(ch
->sources
, child
);
88 * A (*,G) or a (*,*) is being created
89 * find all the children that would point
92 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
94 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
95 struct pim_ifchannel
*child
;
97 // Basic Sanity that we are not being silly
98 if (!pim_addr_is_any(ch
->sg
.src
) && !pim_addr_is_any(ch
->sg
.grp
))
101 if (pim_addr_is_any(ch
->sg
.src
) && pim_addr_is_any(ch
->sg
.grp
))
104 RB_FOREACH (child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
105 if (!pim_addr_is_any(ch
->sg
.grp
) &&
106 !pim_addr_cmp(child
->sg
.grp
, ch
->sg
.grp
) && (child
!= ch
)) {
108 listnode_add_sort(ch
->sources
, child
);
113 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
115 struct pim_interface
*pim_ifp
;
116 struct pim_upstream
*up
;
118 pim_ifp
= ch
->interface
->info
;
120 if (PIM_DEBUG_PIM_TRACE
)
121 zlog_debug("%s: ifchannel entry %s(%s) del start", __func__
,
122 ch
->sg_str
, ch
->interface
->name
);
124 if (PIM_I_am_DualActive(pim_ifp
)) {
127 "%s: if-chnanel-%s is deleted from a Dual active Interface",
128 __func__
, ch
->sg_str
);
129 /* Post Delete only if it is the last Dual-active Interface */
130 if (ch
->upstream
->dualactive_ifchannel_count
== 1) {
131 pim_mlag_up_local_del(pim_ifp
->pim
, ch
->upstream
);
132 PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
133 ch
->upstream
->flags
);
135 ch
->upstream
->dualactive_ifchannel_count
--;
138 if (ch
->upstream
->channel_oil
) {
139 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
140 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
141 mask
|= PIM_OIF_FLAG_PROTO_GM
;
144 * A S,G RPT channel can have an empty oil, we also
145 * need to take into account the fact that a ifchannel
146 * might have been suppressing a *,G ifchannel from
147 * being inherited. So let's figure out what
148 * needs to be done here
150 if (!pim_addr_is_any(ch
->sg
.src
) &&
151 pim_upstream_evaluate_join_desired_interface(
152 ch
->upstream
, ch
, ch
->parent
))
153 pim_channel_add_oif(ch
->upstream
->channel_oil
,
155 PIM_OIF_FLAG_PROTO_STAR
,
158 pim_channel_del_oif(ch
->upstream
->channel_oil
,
159 ch
->interface
, mask
, __func__
);
161 * Do we have any S,G's that are inheriting?
162 * Nuke from on high too.
164 if (ch
->upstream
->sources
) {
165 struct pim_upstream
*child
;
166 struct listnode
*up_node
;
168 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
170 pim_channel_del_inherited_oif(
178 * When this channel is removed
179 * we need to find all our children
180 * and make sure our pointers are fixed
182 pim_ifchannel_remove_children(ch
);
185 list_delete(&ch
->sources
);
187 listnode_delete(ch
->upstream
->ifchannels
, ch
);
191 /* upstream is common across ifchannels, check if upstream's
192 ifchannel list is empty before deleting upstream_del
193 ref count will take care of it.
195 if (ch
->upstream
->ref_count
> 0)
196 up
= pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __func__
);
199 if (PIM_DEBUG_PIM_TRACE
)
201 "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
202 __func__
, ch
->upstream
->ref_count
,
203 ch
->interface
->name
, ch
->sg_str
);
208 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
209 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
210 THREAD_OFF(ch
->t_ifassert_timer
);
213 listnode_delete(ch
->parent
->sources
, ch
);
217 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
219 if (PIM_DEBUG_PIM_TRACE
)
220 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__
,
221 ch
->sg_str
, ch
->interface
->name
);
223 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
226 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
229 void pim_ifchannel_delete_all(struct interface
*ifp
)
231 struct pim_interface
*pim_ifp
;
232 struct pim_ifchannel
*ch
;
238 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
239 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
241 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
242 pim_ifchannel_delete(ch
);
246 void delete_on_noinfo(struct pim_ifchannel
*ch
)
248 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
249 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
250 && ch
->t_ifjoin_expiry_timer
== NULL
)
251 pim_ifchannel_delete(ch
);
254 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
255 enum pim_ifjoin_state new_state
)
257 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
258 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
259 struct pim_ifchannel
*child_ch
;
261 if (PIM_DEBUG_PIM_EVENTS
)
263 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
264 ch
->interface
->name
, ch
->sg_str
,
265 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
266 pim_ifchannel_ifjoin_name(new_state
, 0));
269 if (old_state
== new_state
) {
270 if (PIM_DEBUG_PIM_EVENTS
) {
272 "%s called by %s: non-transition on state %d (%s)",
273 __func__
, caller
, new_state
,
274 pim_ifchannel_ifjoin_name(new_state
, 0));
279 ch
->ifjoin_state
= new_state
;
281 if (pim_addr_is_any(ch
->sg
.src
)) {
282 struct pim_upstream
*up
= ch
->upstream
;
283 struct pim_upstream
*child
;
284 struct listnode
*up_node
;
287 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
288 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
290 struct channel_oil
*c_oil
=
293 if (PIM_DEBUG_PIM_TRACE
)
295 "%s %s: Prune(S,G)=%s from %s",
303 * If the S,G has no if channel and the
305 * has output here then the *,G was
306 * supplying the implied
307 * if channel. So remove it.
309 if (oil_if_has(c_oil
,
310 pim_ifp
->mroute_vif_index
))
311 pim_channel_del_inherited_oif(
312 c_oil
, ch
->interface
,
316 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
317 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
319 if (PIM_DEBUG_PIM_TRACE
)
321 "%s %s: Join(S,G)=%s from %s",
326 /* check if the channel can be
327 * inherited into the SG's OIL
329 child_ch
= pim_ifchannel_find(
332 if (pim_upstream_eval_inherit_if(
333 child
, child_ch
, ch
)) {
337 PIM_OIF_FLAG_PROTO_STAR
,
339 pim_upstream_update_join_desired(
340 pim_ifp
->pim
, child
);
346 /* Transition to/from NOINFO ? */
347 if ((old_state
== PIM_IFJOIN_NOINFO
)
348 || (new_state
== PIM_IFJOIN_NOINFO
)) {
350 if (PIM_DEBUG_PIM_EVENTS
) {
351 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
352 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
354 ch
->sg_str
, ch
->interface
->name
);
358 Record uptime of state transition to/from NOINFO
360 ch
->ifjoin_creation
= pim_time_monotonic_sec();
362 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
363 pim_ifchannel_update_could_assert(ch
);
364 pim_ifchannel_update_assert_tracking_desired(ch
);
368 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
371 switch (ifjoin_state
) {
372 case PIM_IFJOIN_NOINFO
:
373 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
377 case PIM_IFJOIN_JOIN
:
379 case PIM_IFJOIN_PRUNE
:
380 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
384 case PIM_IFJOIN_PRUNE_PENDING
:
385 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
389 case PIM_IFJOIN_PRUNE_TMP
:
390 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
394 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
395 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
401 return "ifjoin_bad_state";
404 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
406 switch (ifassert_state
) {
407 case PIM_IFASSERT_NOINFO
:
409 case PIM_IFASSERT_I_AM_WINNER
:
411 case PIM_IFASSERT_I_AM_LOSER
:
415 return "ifassert_bad_state";
419 RFC 4601: 4.6.5. Assert State Macros
421 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
422 defaults to Infinity when in the NoInfo state.
424 void reset_ifassert_state(struct pim_ifchannel
*ch
)
426 THREAD_OFF(ch
->t_ifassert_timer
);
428 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, PIMADDR_ANY
,
429 router
->infinite_assert_metric
);
432 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
, pim_sgaddr
*sg
)
434 struct pim_interface
*pim_ifp
;
435 struct pim_ifchannel
*ch
;
436 struct pim_ifchannel lookup
;
441 zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
442 __func__
, sg
, ifp
->name
);
447 lookup
.interface
= ifp
;
448 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
453 static void ifmembership_set(struct pim_ifchannel
*ch
,
454 enum pim_ifmembership membership
)
456 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
458 if (ch
->local_ifmembership
== membership
)
461 if (PIM_DEBUG_PIM_EVENTS
) {
462 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
463 __func__
, ch
->sg_str
,
464 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
466 ch
->interface
->name
);
469 ch
->local_ifmembership
= membership
;
471 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
472 pim_ifchannel_update_could_assert(ch
);
473 pim_ifchannel_update_assert_tracking_desired(ch
);
477 void pim_ifchannel_membership_clear(struct interface
*ifp
)
479 struct pim_interface
*pim_ifp
;
480 struct pim_ifchannel
*ch
;
485 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
486 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
489 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
491 struct pim_interface
*pim_ifp
;
492 struct pim_ifchannel
*ch
, *ch_tmp
;
497 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
498 delete_on_noinfo(ch
);
502 * For a given Interface, if we are given a S,G
503 * Find the *,G (If we have it).
504 * If we are passed a *,G, find the *,* ifchannel
507 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
509 pim_sgaddr parent_sg
= ch
->sg
;
510 struct pim_ifchannel
*parent
= NULL
;
513 if (!pim_addr_is_any(parent_sg
.src
) &&
514 !pim_addr_is_any(parent_sg
.grp
)) {
515 parent_sg
.src
= PIMADDR_ANY
;
516 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
519 listnode_add(parent
->sources
, ch
);
526 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
, pim_sgaddr
*sg
,
527 uint8_t source_flags
, int up_flags
)
529 struct pim_interface
*pim_ifp
;
530 struct pim_ifchannel
*ch
;
531 struct pim_upstream
*up
;
533 ch
= pim_ifchannel_find(ifp
, sg
);
535 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
536 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
538 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
539 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
542 ch
->upstream
->flags
|= up_flags
;
543 else if (PIM_DEBUG_EVENTS
)
544 zlog_debug("%s:%pSG No Upstream found", __func__
, sg
);
551 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
554 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
555 && !(source_flags
& PIM_ENCODE_WC_BIT
))
556 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
560 snprintfrr(ch
->sg_str
, sizeof(ch
->sg_str
), "%pSG", sg
);
561 ch
->parent
= pim_ifchannel_find_parent(ch
);
562 if (pim_addr_is_any(ch
->sg
.src
)) {
563 ch
->sources
= list_new();
565 (int (*)(void *, void *))pim_ifchannel_compare
;
569 pim_ifchannel_find_new_children(ch
);
570 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
572 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
573 ch
->t_ifjoin_expiry_timer
= NULL
;
574 ch
->t_ifjoin_prune_pending_timer
= NULL
;
575 ch
->ifjoin_creation
= 0;
577 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
579 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
, __func__
, ch
);
583 listnode_add_sort(up
->ifchannels
, ch
);
585 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
586 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
588 ch
->ifassert_winner
= PIMADDR_ANY
;
591 ch
->t_ifassert_timer
= NULL
;
592 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
593 reset_ifassert_state(ch
);
594 if (pim_macro_ch_could_assert_eval(ch
))
595 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
597 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
599 if (pim_macro_assert_tracking_desired_eval(ch
))
600 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
602 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
605 * advertise MLAG Data to MLAG peer
607 if (PIM_I_am_DualActive(pim_ifp
)) {
608 up
->dualactive_ifchannel_count
++;
609 /* Sync once for upstream */
610 if (up
->dualactive_ifchannel_count
== 1) {
611 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up
->flags
);
612 pim_mlag_up_local_add(pim_ifp
->pim
, up
);
616 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
617 __func__
, up
->sg_str
,
618 up
->dualactive_ifchannel_count
, up
->flags
);
621 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
622 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
624 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
625 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
627 if (PIM_DEBUG_PIM_TRACE
)
628 zlog_debug("%s: ifchannel %s(%s) is created ", __func__
,
629 ch
->sg_str
, ch
->interface
->name
);
634 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
)
636 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
637 pim_forward_stop(ch
);
640 PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch
->upstream
->flags
);
642 PIM_IF_FLAG_UNSET_PROTO_PIM(ch
->flags
);
644 delete_on_noinfo(ch
);
647 static void on_ifjoin_expiry_timer(struct thread
*t
)
649 struct pim_ifchannel
*ch
;
653 if (PIM_DEBUG_PIM_TRACE
)
654 zlog_debug("%s: ifchannel %s expiry timer", __func__
,
657 ifjoin_to_noinfo(ch
);
658 /* ch may have been deleted */
661 static void on_ifjoin_prune_pending_timer(struct thread
*t
)
663 struct pim_ifchannel
*ch
;
664 int send_prune_echo
; /* boolean */
665 struct interface
*ifp
;
666 struct pim_interface
*pim_ifp
;
670 if (PIM_DEBUG_PIM_TRACE
)
671 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
673 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
675 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
678 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
679 /* Send PruneEcho(S,G) ? */
681 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
683 if (send_prune_echo
) {
686 rpf
.source_nexthop
.interface
= ifp
;
687 pim_addr_to_prefix(&rpf
.rpf_addr
,
688 pim_ifp
->primary_address
);
689 pim_jp_agg_single_upstream_send(
690 &rpf
, ch
->upstream
, 0);
693 ifjoin_to_noinfo(ch
);
695 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
696 * message on RP path upon prune timer expiry.
698 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
700 struct pim_upstream
*parent
=
701 ch
->upstream
->parent
;
703 pim_upstream_update_join_desired(pim_ifp
->pim
,
706 pim_jp_agg_single_upstream_send(&parent
->rpf
,
709 * SGRpt prune pending expiry has to install
710 * SG entry with empty olist to drop the SG
711 * traffic incase no other intf exists.
712 * On that scenario, SG entry wouldn't have
713 * got installed until Prune pending timer
714 * expired. So install now.
717 ch
->upstream
->channel_oil
, ifp
,
718 PIM_OIF_FLAG_PROTO_STAR
, __func__
);
719 if (!ch
->upstream
->channel_oil
->installed
)
720 pim_upstream_mroute_add(
721 ch
->upstream
->channel_oil
,
725 /* from here ch may have been deleted */
729 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
730 pim_addr upstream
, pim_sgaddr
*sg
,
731 uint8_t source_flags
, int holdtime
)
733 struct pim_upstream
*up
;
734 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
737 /* Upstream (S,G) in Joined state ? */
738 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
741 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
744 /* Upstream (S,G) in Joined state */
746 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
747 /* RPF'(S,G) not found */
748 zlog_warn("%s %s: RPF'%s not found", __FILE__
, __func__
,
753 rpf_addr
= pim_addr_from_prefix(&up
->rpf
.rpf_addr
);
755 /* upstream directed to RPF'(S,G) ? */
756 if (pim_addr_cmp(upstream
, rpf_addr
)) {
758 "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
759 __FILE__
, __func__
, up
->sg_str
, &upstream
, &rpf_addr
,
763 /* upstream directed to RPF'(S,G) */
766 /* Join(S,G) to RPF'(S,G) */
767 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
, holdtime
);
771 /* Prune to RPF'(S,G) */
773 if (source_flags
& PIM_RPT_BIT_MASK
) {
774 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
775 /* Prune(*,G) to RPF'(S,G) */
776 pim_upstream_join_timer_decrease_to_t_override(
781 /* Prune(S,G,rpt) to RPF'(S,G) */
782 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
787 /* Prune(S,G) to RPF'(S,G) */
788 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
791 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
792 pim_addr upstream
, pim_sgaddr
*sg
,
793 uint8_t source_flags
, uint16_t holdtime
)
795 struct pim_interface
*recv_pim_ifp
;
796 int is_local
; /* boolean */
798 recv_pim_ifp
= recv_ifp
->info
;
799 assert(recv_pim_ifp
);
801 is_local
= !pim_addr_cmp(upstream
, recv_pim_ifp
->primary_address
);
806 if (PIM_DEBUG_PIM_TRACE_DETAIL
)
808 "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
809 __func__
, is_join
? "join" : "prune", sg
, &upstream
,
813 * Since recv upstream addr was not directed to our primary
814 * address, check if we should react to it in any way.
816 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
819 return 1; /* non-local */
822 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel
*ch
,
823 struct pim_interface
*pim_ifp
)
825 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
826 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
827 /* check if the interface qualifies as an immediate
830 if (pim_upstream_evaluate_join_desired_interface(
833 pim_channel_add_oif(ch
->upstream
->channel_oil
,
835 PIM_OIF_FLAG_PROTO_PIM
,
837 pim_upstream_update_join_desired(pim_ifp
->pim
,
843 void pim_ifchannel_join_add(struct interface
*ifp
, pim_addr neigh_addr
,
844 pim_addr upstream
, pim_sgaddr
*sg
,
845 uint8_t source_flags
, uint16_t holdtime
)
847 struct pim_interface
*pim_ifp
;
848 struct pim_ifchannel
*ch
;
850 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
855 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
856 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
859 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
861 Transitions from "I am Assert Loser" State
863 Receive Join(S,G) on Interface I
865 We receive a Join(S,G) that has the Upstream Neighbor Address
866 field set to my primary IP address on interface I. The action is
867 to transition to NoInfo state, delete this (S,G) assert state
868 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
871 Notice: The nonlocal_upstream() test above ensures the upstream
872 address of the join message is our primary address.
874 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
875 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
876 __func__
, ch
->sg_str
, &neigh_addr
, ifp
->name
);
878 assert_action_a5(ch
);
884 switch (ch
->ifjoin_state
) {
885 case PIM_IFJOIN_NOINFO
:
886 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
887 if (pim_macro_chisin_oiflist(ch
)) {
888 pim_upstream_inherited_olist(pim_ifp
->pim
,
890 pim_forward_start(ch
);
893 * If we are going to be a LHR, we need to note it
895 if (ch
->upstream
->parent
&&
896 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
897 ch
->upstream
->parent
->flags
))
898 && !(ch
->upstream
->flags
899 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
900 pim_upstream_ref(ch
->upstream
,
901 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
903 pim_upstream_keep_alive_timer_start(
904 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
907 case PIM_IFJOIN_JOIN
:
908 assert(!ch
->t_ifjoin_prune_pending_timer
);
911 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
913 previously received join message with holdtime=0xFFFF.
915 if (ch
->t_ifjoin_expiry_timer
) {
916 unsigned long remain
= thread_timer_remain_second(
917 ch
->t_ifjoin_expiry_timer
);
918 if (remain
> holdtime
) {
920 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
923 Transitions from Join State
925 The (S,G) downstream state machine on
926 interface I remains in
927 Join state, and the Expiry Timer (ET) is
929 maximum of its current value and the HoldTime
931 triggering Join/Prune message.
933 Conclusion: Do not change the ET if the
935 higher than the received join holdtime.
940 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
942 case PIM_IFJOIN_PRUNE
:
943 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
944 pim_ifchannel_ifjoin_switch(__func__
, ch
,
946 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
947 delete_on_noinfo(ch
);
950 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
952 case PIM_IFJOIN_PRUNE_PENDING
:
954 * Transitions from Prune-Pending State (Receive Join)
955 * RFC 7761 Sec 4.5.2:
956 * The (S,G) downstream state machine on interface I
957 * transitions to the Join state. The Prune-Pending Timer is
958 * canceled (without triggering an expiry event). The
959 * Expiry Timer (ET) is restarted and is then set to the
960 * maximum of its current value and the HoldTime from the
961 * triggering Join/Prune message.
963 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
965 /* Check if SGRpt join Received */
966 if ((source_flags
& PIM_ENCODE_RPT_BIT
) &&
967 !pim_addr_is_any(sg
->src
)) {
969 * Transitions from Prune-Pending State (Rcv SGRpt Join)
970 * RFC 7761 Sec 4.5.3:
971 * The (S,G,rpt) downstream state machine on interface
972 * I transitions to the NoInfo state.The ET and PPT are
975 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
976 pim_ifchannel_ifjoin_switch(__func__
, ch
,
981 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
983 if (ch
->t_ifjoin_expiry_timer
) {
984 unsigned long remain
= thread_timer_remain_second(
985 ch
->t_ifjoin_expiry_timer
);
987 if (remain
> holdtime
)
990 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
993 case PIM_IFJOIN_PRUNE_TMP
:
995 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
999 if (holdtime
!= 0xFFFF) {
1000 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
1001 holdtime
, &ch
->t_ifjoin_expiry_timer
);
1005 void pim_ifchannel_prune(struct interface
*ifp
, pim_addr upstream
,
1006 pim_sgaddr
*sg
, uint8_t source_flags
,
1009 struct pim_ifchannel
*ch
;
1010 struct pim_interface
*pim_ifp
;
1011 int jp_override_interval_msec
;
1013 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
1018 ch
= pim_ifchannel_find(ifp
, sg
);
1019 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
1020 if (PIM_DEBUG_PIM_TRACE
)
1021 zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1022 __func__
, ifp
->name
, sg
,
1027 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
1028 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
1030 pim_ifp
= ifp
->info
;
1032 switch (ch
->ifjoin_state
) {
1033 case PIM_IFJOIN_NOINFO
:
1034 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1035 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
1036 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
1038 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1039 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1040 jp_override_interval_msec
=
1041 pim_if_jp_override_interval_msec(ifp
);
1043 jp_override_interval_msec
=
1044 0; /* schedule to expire immediately */
1045 /* If we called ifjoin_prune() directly instead, care
1047 be taken not to use "ch" afterwards since it would be
1050 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1051 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1052 thread_add_timer_msec(
1053 router
->master
, on_ifjoin_prune_pending_timer
,
1054 ch
, jp_override_interval_msec
,
1055 &ch
->t_ifjoin_prune_pending_timer
);
1056 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1058 &ch
->t_ifjoin_expiry_timer
);
1059 pim_upstream_update_join_desired(pim_ifp
->pim
,
1063 case PIM_IFJOIN_PRUNE_PENDING
:
1066 case PIM_IFJOIN_JOIN
:
1068 * The (S,G) downstream state machine on interface I
1069 * transitions to the Prune-Pending state. The
1070 * Prune-Pending Timer is started. It is set to the
1071 * J/P_Override_Interval(I) if the router has more than one
1072 * neighbor on that interface; otherwise, it is set to zero,
1073 * causing it to expire immediately.
1076 pim_ifchannel_ifjoin_switch(__func__
, ch
,
1077 PIM_IFJOIN_PRUNE_PENDING
);
1079 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1080 jp_override_interval_msec
=
1081 pim_if_jp_override_interval_msec(ifp
);
1083 jp_override_interval_msec
=
1084 0; /* schedule to expire immediately */
1085 /* If we called ifjoin_prune() directly instead, care should
1086 be taken not to use "ch" afterwards since it would be
1088 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1089 thread_add_timer_msec(router
->master
,
1090 on_ifjoin_prune_pending_timer
, ch
,
1091 jp_override_interval_msec
,
1092 &ch
->t_ifjoin_prune_pending_timer
);
1094 case PIM_IFJOIN_PRUNE
:
1095 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1096 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1098 * While in Prune State, Receive SGRpt Prune.
1099 * RFC 7761 Sec 4.5.3:
1100 * The (S,G,rpt) downstream state machine on interface I
1101 * remains in Prune state. The Expiry Timer (ET) is
1102 * restarted and is then set to the maximum of its
1103 * current value and the HoldTime from the triggering
1104 * Join/Prune message.
1106 if (ch
->t_ifjoin_expiry_timer
) {
1107 unsigned long rem
= thread_timer_remain_second(
1108 ch
->t_ifjoin_expiry_timer
);
1112 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1115 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1117 &ch
->t_ifjoin_expiry_timer
);
1120 case PIM_IFJOIN_PRUNE_TMP
:
1121 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1122 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1123 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1124 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1126 &ch
->t_ifjoin_expiry_timer
);
1129 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1130 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1131 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1132 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1133 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1135 &ch
->t_ifjoin_expiry_timer
);
1141 int pim_ifchannel_local_membership_add(struct interface
*ifp
, pim_sgaddr
*sg
,
1144 struct pim_ifchannel
*ch
, *starch
;
1145 struct pim_interface
*pim_ifp
;
1146 struct pim_instance
*pim
;
1149 /* PIM enabled on interface? */
1150 pim_ifp
= ifp
->info
;
1152 if (PIM_DEBUG_EVENTS
)
1153 zlog_debug("%s:%pSG Expected pim interface setup for %s",
1154 __func__
, sg
, ifp
->name
);
1158 if (!pim_ifp
->pim_enable
) {
1159 if (PIM_DEBUG_EVENTS
)
1160 zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1161 __func__
, sg
, ifp
->name
);
1167 /* skip (*,G) ch creation if G is of type SSM */
1168 if (pim_addr_is_any(sg
->src
)) {
1169 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1170 if (PIM_DEBUG_PIM_EVENTS
)
1171 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1177 /* vxlan term mroutes use ipmr-lo as local member to
1178 * pull down multicast vxlan tunnel traffic
1180 up_flags
= is_vxlan
? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
:
1181 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
;
1182 ch
= pim_ifchannel_add(ifp
, sg
, 0, up_flags
);
1184 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1186 if (pim_addr_is_any(sg
->src
)) {
1187 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1188 struct pim_upstream
*child
;
1189 struct listnode
*up_node
;
1193 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1194 if (PIM_DEBUG_EVENTS
)
1195 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1196 __FILE__
, __func__
, child
->sg_str
,
1197 ifp
->name
, up
->sg_str
);
1199 if (!child
->rpf
.source_nexthop
.interface
) {
1200 /* when iif unknown, do not inherit */
1201 if (PIM_DEBUG_EVENTS
)
1203 "Skipped (S,G)=%s(%s) from %s: no iif",
1204 child
->sg_str
, ifp
->name
,
1209 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1210 if (pim_upstream_evaluate_join_desired_interface(
1211 child
, ch
, starch
)) {
1212 pim_channel_add_oif(child
->channel_oil
, ifp
,
1213 PIM_OIF_FLAG_PROTO_STAR
,
1215 pim_upstream_update_join_desired(pim
, child
);
1219 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1220 if (pim
->spt
.plist
) {
1221 struct prefix_list
*plist
= prefix_list_lookup(
1222 AFI_IP
, pim
->spt
.plist
);
1225 pim_addr_to_prefix(&g
, up
->sg
.grp
);
1226 if (prefix_list_apply(plist
, &g
)
1228 pim_channel_add_oif(
1229 up
->channel_oil
, pim
->regiface
,
1230 PIM_OIF_FLAG_PROTO_GM
,
1235 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1236 PIM_OIF_FLAG_PROTO_GM
, __func__
);
1242 void pim_ifchannel_local_membership_del(struct interface
*ifp
, pim_sgaddr
*sg
)
1244 struct pim_ifchannel
*starch
, *ch
, *orig
;
1245 struct pim_interface
*pim_ifp
;
1247 /* PIM enabled on interface? */
1248 pim_ifp
= ifp
->info
;
1251 if (!pim_ifp
->pim_enable
)
1254 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1257 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1259 if (pim_addr_is_any(sg
->src
)) {
1260 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1261 struct pim_upstream
*child
;
1262 struct listnode
*up_node
, *up_nnode
;
1266 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1267 struct channel_oil
*c_oil
= child
->channel_oil
;
1268 struct pim_ifchannel
*chchannel
=
1269 pim_ifchannel_find(ifp
, &child
->sg
);
1271 pim_ifp
= ifp
->info
;
1273 if (PIM_DEBUG_EVENTS
)
1274 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1275 __FILE__
, __func__
, up
->sg_str
,
1276 ifp
->name
, child
->sg_str
);
1278 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1280 * If the S,G has no if channel and the c_oil still
1281 * has output here then the *,G was supplying the
1283 * if channel. So remove it.
1285 if (!pim_upstream_evaluate_join_desired_interface(
1286 child
, ch
, starch
) ||
1288 oil_if_has(c_oil
, pim_ifp
->mroute_vif_index
))) {
1289 pim_channel_del_inherited_oif(c_oil
, ifp
,
1293 /* Child node removal/ref count-- will happen as part of
1294 * parent' delete_no_info */
1298 /* Resettng the IGMP flags here */
1300 PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig
->upstream
->flags
);
1302 PIM_IF_FLAG_UNSET_PROTO_IGMP(orig
->flags
);
1304 delete_on_noinfo(orig
);
1307 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1309 int old_couldassert
=
1310 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1311 int new_couldassert
=
1312 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1314 if (new_couldassert
== old_couldassert
)
1317 if (PIM_DEBUG_PIM_EVENTS
)
1318 zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1319 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
,
1320 ch
->interface
->name
, old_couldassert
,
1323 if (new_couldassert
) {
1324 /* CouldAssert(S,G,I) switched from false to true */
1325 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1327 /* CouldAssert(S,G,I) switched from true to false */
1328 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1330 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1331 assert_action_a4(ch
);
1335 pim_ifchannel_update_my_assert_metric(ch
);
1339 my_assert_metric may be affected by:
1342 pim_ifp->primary_address
1343 rpf->source_nexthop.mrib_metric_preference;
1344 rpf->source_nexthop.mrib_route_metric;
1346 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1348 struct pim_assert_metric my_metric_new
=
1349 pim_macro_ch_my_assert_metric_eval(ch
);
1351 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1354 if (PIM_DEBUG_PIM_EVENTS
)
1356 "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1357 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1358 ch
->ifassert_my_metric
.rpt_bit_flag
,
1359 ch
->ifassert_my_metric
.metric_preference
,
1360 ch
->ifassert_my_metric
.route_metric
,
1361 &ch
->ifassert_my_metric
.ip_address
,
1362 my_metric_new
.rpt_bit_flag
,
1363 my_metric_new
.metric_preference
,
1364 my_metric_new
.route_metric
, &my_metric_new
.ip_address
);
1366 ch
->ifassert_my_metric
= my_metric_new
;
1368 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1369 &ch
->ifassert_winner_metric
)) {
1370 assert_action_a5(ch
);
1374 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1376 int old_atd
= PIM_FORCE_BOOLEAN(
1377 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1379 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1381 if (new_atd
== old_atd
)
1384 if (PIM_DEBUG_PIM_EVENTS
)
1386 "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1387 __func__
, &ch
->sg
.src
, &ch
->sg
.grp
, ch
->interface
->name
,
1391 /* AssertTrackingDesired(S,G,I) switched from false to true */
1392 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1394 /* AssertTrackingDesired(S,G,I) switched from true to false */
1395 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1397 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1398 assert_action_a5(ch
);
1404 * If we have a new pim interface, check to
1405 * see if any of the pre-existing channels have
1406 * their upstream out that way and turn on forwarding
1407 * for that ifchannel then.
1409 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1411 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1412 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1413 struct interface
*ifp
;
1415 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1416 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1417 struct pim_ifchannel
*ch
;
1422 if (new_pim_ifp
== loop_pim_ifp
)
1425 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1426 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1427 struct pim_upstream
*up
= ch
->upstream
;
1428 if ((!up
->channel_oil
)
1429 && (up
->rpf
.source_nexthop
1430 .interface
== new_ifp
))
1431 pim_forward_start(ch
);
1438 * Downstream per-interface (S,G,rpt) state machine
1439 * states that we need to move (S,G,rpt) items
1440 * into different states at the start of the
1441 * reception of a *,G join as well, when
1442 * we get End of Message
1444 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1447 bool send_upstream_starg
= false;
1448 struct pim_ifchannel
*child
;
1449 struct listnode
*ch_node
, *nch_node
;
1450 struct pim_instance
*pim
=
1451 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1452 struct pim_upstream
*starup
= ch
->upstream
;
1454 if (PIM_DEBUG_PIM_TRACE
)
1456 "%s: %s %s eom: %d join %u", __func__
,
1457 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1458 ch
->sg_str
, eom
, join
);
1462 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1463 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1466 switch (child
->ifjoin_state
) {
1467 case PIM_IFJOIN_NOINFO
:
1468 case PIM_IFJOIN_JOIN
:
1470 case PIM_IFJOIN_PRUNE
:
1472 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1474 case PIM_IFJOIN_PRUNE_PENDING
:
1476 child
->ifjoin_state
=
1477 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1479 case PIM_IFJOIN_PRUNE_TMP
:
1480 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1484 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1485 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1486 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1488 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1489 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1491 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1492 (!pim_upstream_empty_inherited_olist(
1493 child
->upstream
))) {
1494 pim_channel_add_oif(
1495 child
->upstream
->channel_oil
,
1496 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
,
1498 pim_upstream_update_join_desired(pim
,
1501 send_upstream_starg
= true;
1503 delete_on_noinfo(child
);
1508 if (send_upstream_starg
)
1509 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);