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 if (ntohl(ch1
->sg
.grp
.s_addr
) < ntohl(ch2
->sg
.grp
.s_addr
))
68 if (ntohl(ch1
->sg
.grp
.s_addr
) > ntohl(ch2
->sg
.grp
.s_addr
))
71 if (ntohl(ch1
->sg
.src
.s_addr
) < ntohl(ch2
->sg
.src
.s_addr
))
74 if (ntohl(ch1
->sg
.src
.s_addr
) > ntohl(ch2
->sg
.src
.s_addr
))
81 * A (*,G) or a (*,*) is going away
82 * remove the parent pointer from
83 * those pointing at us
85 static void pim_ifchannel_remove_children(struct pim_ifchannel
*ch
)
87 struct pim_ifchannel
*child
;
92 while (!list_isempty(ch
->sources
)) {
93 child
= listnode_head(ch
->sources
);
95 listnode_delete(ch
->sources
, child
);
100 * A (*,G) or a (*,*) is being created
101 * find all the children that would point
104 static void pim_ifchannel_find_new_children(struct pim_ifchannel
*ch
)
106 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
107 struct pim_ifchannel
*child
;
109 // Basic Sanity that we are not being silly
110 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
)
111 && (ch
->sg
.grp
.s_addr
!= INADDR_ANY
))
114 if ((ch
->sg
.src
.s_addr
== INADDR_ANY
)
115 && (ch
->sg
.grp
.s_addr
== INADDR_ANY
))
118 RB_FOREACH (child
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
) {
119 if ((ch
->sg
.grp
.s_addr
!= INADDR_ANY
)
120 && (child
->sg
.grp
.s_addr
== ch
->sg
.grp
.s_addr
)
123 listnode_add_sort(ch
->sources
, child
);
128 void pim_ifchannel_delete(struct pim_ifchannel
*ch
)
130 struct pim_interface
*pim_ifp
;
131 struct pim_upstream
*up
;
133 pim_ifp
= ch
->interface
->info
;
135 if (PIM_DEBUG_PIM_TRACE
)
136 zlog_debug("%s: ifchannel entry %s(%s) del start", __func__
,
137 ch
->sg_str
, ch
->interface
->name
);
139 if (PIM_I_am_DualActive(pim_ifp
)) {
142 "%s: if-chnanel-%s is deleted from a Dual active Interface",
143 __func__
, ch
->sg_str
);
144 /* Post Delete only if it is the last Dual-active Interface */
145 if (ch
->upstream
->dualactive_ifchannel_count
== 1) {
146 pim_mlag_up_local_del(pim_ifp
->pim
, ch
->upstream
);
147 PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
148 ch
->upstream
->flags
);
150 ch
->upstream
->dualactive_ifchannel_count
--;
153 if (ch
->upstream
->channel_oil
) {
154 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
155 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
156 mask
|= PIM_OIF_FLAG_PROTO_IGMP
;
159 * A S,G RPT channel can have an empty oil, we also
160 * need to take into account the fact that a ifchannel
161 * might have been suppressing a *,G ifchannel from
162 * being inherited. So let's figure out what
163 * needs to be done here
165 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
166 pim_upstream_evaluate_join_desired_interface(
167 ch
->upstream
, ch
, ch
->parent
))
168 pim_channel_add_oif(ch
->upstream
->channel_oil
,
170 PIM_OIF_FLAG_PROTO_STAR
,
173 pim_channel_del_oif(ch
->upstream
->channel_oil
,
174 ch
->interface
, mask
, __func__
);
176 * Do we have any S,G's that are inheriting?
177 * Nuke from on high too.
179 if (ch
->upstream
->sources
) {
180 struct pim_upstream
*child
;
181 struct listnode
*up_node
;
183 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
185 pim_channel_del_inherited_oif(
193 * When this channel is removed
194 * we need to find all our children
195 * and make sure our pointers are fixed
197 pim_ifchannel_remove_children(ch
);
200 list_delete(&ch
->sources
);
202 listnode_delete(ch
->upstream
->ifchannels
, ch
);
206 /* upstream is common across ifchannels, check if upstream's
207 ifchannel list is empty before deleting upstream_del
208 ref count will take care of it.
210 if (ch
->upstream
->ref_count
> 0)
211 up
= pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __func__
);
214 if (PIM_DEBUG_PIM_TRACE
)
216 "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
217 __func__
, ch
->upstream
->ref_count
,
218 ch
->interface
->name
, ch
->sg_str
);
223 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
224 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
225 THREAD_OFF(ch
->t_ifassert_timer
);
228 listnode_delete(ch
->parent
->sources
, ch
);
232 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
234 if (PIM_DEBUG_PIM_TRACE
)
235 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__
,
236 ch
->sg_str
, ch
->interface
->name
);
238 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
241 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
244 void pim_ifchannel_delete_all(struct interface
*ifp
)
246 struct pim_interface
*pim_ifp
;
247 struct pim_ifchannel
*ch
;
253 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
254 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
256 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
257 pim_ifchannel_delete(ch
);
261 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
263 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
264 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
265 && ch
->t_ifjoin_expiry_timer
== NULL
)
266 pim_ifchannel_delete(ch
);
269 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
270 enum pim_ifjoin_state new_state
)
272 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
273 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
274 struct pim_ifchannel
*child_ch
;
276 if (PIM_DEBUG_PIM_EVENTS
)
278 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
279 ch
->interface
->name
, ch
->sg_str
,
280 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
281 pim_ifchannel_ifjoin_name(new_state
, 0));
284 if (old_state
== new_state
) {
285 if (PIM_DEBUG_PIM_EVENTS
) {
287 "%s calledby %s: non-transition on state %d (%s)",
288 __func__
, caller
, new_state
,
289 pim_ifchannel_ifjoin_name(new_state
, 0));
294 ch
->ifjoin_state
= new_state
;
296 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
297 struct pim_upstream
*up
= ch
->upstream
;
298 struct pim_upstream
*child
;
299 struct listnode
*up_node
;
302 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
303 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
305 struct channel_oil
*c_oil
=
308 if (PIM_DEBUG_PIM_TRACE
)
310 "%s %s: Prune(S,G)=%s from %s",
318 * If the S,G has no if channel and the
320 * has output here then the *,G was
321 * supplying the implied
322 * if channel. So remove it.
324 if (c_oil
->oil
.mfcc_ttls
325 [pim_ifp
->mroute_vif_index
])
326 pim_channel_del_inherited_oif(
327 c_oil
, ch
->interface
,
331 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
332 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
334 if (PIM_DEBUG_PIM_TRACE
)
336 "%s %s: Join(S,G)=%s from %s",
341 /* check if the channel can be
342 * inherited into the SG's OIL
344 child_ch
= pim_ifchannel_find(
347 if (pim_upstream_eval_inherit_if(
348 child
, child_ch
, ch
)) {
352 PIM_OIF_FLAG_PROTO_STAR
,
354 pim_upstream_update_join_desired(
355 pim_ifp
->pim
, child
);
361 /* Transition to/from NOINFO ? */
362 if ((old_state
== PIM_IFJOIN_NOINFO
)
363 || (new_state
== PIM_IFJOIN_NOINFO
)) {
365 if (PIM_DEBUG_PIM_EVENTS
) {
366 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
367 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
369 ch
->sg_str
, ch
->interface
->name
);
373 Record uptime of state transition to/from NOINFO
375 ch
->ifjoin_creation
= pim_time_monotonic_sec();
377 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
378 pim_ifchannel_update_could_assert(ch
);
379 pim_ifchannel_update_assert_tracking_desired(ch
);
383 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
386 switch (ifjoin_state
) {
387 case PIM_IFJOIN_NOINFO
:
388 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
392 case PIM_IFJOIN_JOIN
:
394 case PIM_IFJOIN_PRUNE
:
395 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
399 case PIM_IFJOIN_PRUNE_PENDING
:
400 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
404 case PIM_IFJOIN_PRUNE_TMP
:
405 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
409 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
410 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
416 return "ifjoin_bad_state";
419 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
421 switch (ifassert_state
) {
422 case PIM_IFASSERT_NOINFO
:
424 case PIM_IFASSERT_I_AM_WINNER
:
426 case PIM_IFASSERT_I_AM_LOSER
:
430 return "ifassert_bad_state";
434 RFC 4601: 4.6.5. Assert State Macros
436 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
437 defaults to Infinity when in the NoInfo state.
439 void reset_ifassert_state(struct pim_ifchannel
*ch
)
441 struct in_addr any
= {.s_addr
= INADDR_ANY
};
443 THREAD_OFF(ch
->t_ifassert_timer
);
445 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
446 router
->infinite_assert_metric
);
449 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
450 struct prefix_sg
*sg
)
452 struct pim_interface
*pim_ifp
;
453 struct pim_ifchannel
*ch
;
454 struct pim_ifchannel lookup
;
459 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
460 __func__
, pim_str_sg_dump(sg
), ifp
->name
);
465 lookup
.interface
= ifp
;
466 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
471 static void ifmembership_set(struct pim_ifchannel
*ch
,
472 enum pim_ifmembership membership
)
474 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
476 if (ch
->local_ifmembership
== membership
)
479 if (PIM_DEBUG_PIM_EVENTS
) {
480 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
481 __func__
, ch
->sg_str
,
482 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
484 ch
->interface
->name
);
487 ch
->local_ifmembership
= membership
;
489 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
490 pim_ifchannel_update_could_assert(ch
);
491 pim_ifchannel_update_assert_tracking_desired(ch
);
495 void pim_ifchannel_membership_clear(struct interface
*ifp
)
497 struct pim_interface
*pim_ifp
;
498 struct pim_ifchannel
*ch
;
503 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
504 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
507 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
509 struct pim_interface
*pim_ifp
;
510 struct pim_ifchannel
*ch
, *ch_tmp
;
515 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
516 delete_on_noinfo(ch
);
520 * For a given Interface, if we are given a S,G
521 * Find the *,G (If we have it).
522 * If we are passed a *,G, find the *,* ifchannel
525 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
527 struct prefix_sg parent_sg
= ch
->sg
;
528 struct pim_ifchannel
*parent
= NULL
;
531 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
532 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
533 parent_sg
.src
.s_addr
= INADDR_ANY
;
534 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
537 listnode_add(parent
->sources
, ch
);
544 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
545 struct prefix_sg
*sg
,
546 uint8_t source_flags
, int up_flags
)
548 struct pim_interface
*pim_ifp
;
549 struct pim_ifchannel
*ch
;
550 struct pim_upstream
*up
;
552 ch
= pim_ifchannel_find(ifp
, sg
);
558 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
561 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
562 && !(source_flags
& PIM_ENCODE_WC_BIT
))
563 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
567 pim_str_sg_set(sg
, ch
->sg_str
);
568 ch
->parent
= pim_ifchannel_find_parent(ch
);
569 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
570 ch
->sources
= list_new();
572 (int (*)(void *, void *))pim_ifchannel_compare
;
576 pim_ifchannel_find_new_children(ch
);
577 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
579 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
580 ch
->t_ifjoin_expiry_timer
= NULL
;
581 ch
->t_ifjoin_prune_pending_timer
= NULL
;
582 ch
->ifjoin_creation
= 0;
584 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
586 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
, __func__
, ch
);
590 listnode_add_sort(up
->ifchannels
, ch
);
592 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
593 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
595 ch
->ifassert_winner
.s_addr
= INADDR_ANY
;
598 ch
->t_ifassert_timer
= NULL
;
599 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
600 reset_ifassert_state(ch
);
601 if (pim_macro_ch_could_assert_eval(ch
))
602 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
604 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
606 if (pim_macro_assert_tracking_desired_eval(ch
))
607 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
609 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
612 * advertise MLAG Data to MLAG peer
614 if (PIM_I_am_DualActive(pim_ifp
)) {
615 up
->dualactive_ifchannel_count
++;
616 /* Sync once for upstream */
617 if (up
->dualactive_ifchannel_count
== 1) {
618 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up
->flags
);
619 pim_mlag_up_local_add(pim_ifp
->pim
, up
);
623 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
624 __func__
, up
->sg_str
,
625 up
->dualactive_ifchannel_count
, up
->flags
);
628 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
629 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
631 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
632 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
634 if (PIM_DEBUG_PIM_TRACE
)
635 zlog_debug("%s: ifchannel %s(%s) is created ", __func__
,
636 ch
->sg_str
, ch
->interface
->name
);
641 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
643 pim_forward_stop(ch
, !ch_del
);
644 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
646 delete_on_noinfo(ch
);
649 static int on_ifjoin_expiry_timer(struct thread
*t
)
651 struct pim_ifchannel
*ch
;
655 if (PIM_DEBUG_PIM_TRACE
)
656 zlog_debug("%s: ifchannel %s expiry timer", __func__
,
659 ifjoin_to_noinfo(ch
, true);
660 /* ch may have been deleted */
665 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
667 struct pim_ifchannel
*ch
;
668 int send_prune_echo
; /* boolean */
669 struct interface
*ifp
;
670 struct pim_interface
*pim_ifp
;
674 if (PIM_DEBUG_PIM_TRACE
)
676 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
677 __func__
, pim_str_sg_dump(&ch
->sg
),
678 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
680 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
683 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
684 /* Send PruneEcho(S,G) ? */
686 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
688 if (send_prune_echo
) {
691 rpf
.source_nexthop
.interface
= ifp
;
692 rpf
.rpf_addr
.u
.prefix4
=
693 pim_ifp
->primary_address
;
694 pim_jp_agg_single_upstream_send(
695 &rpf
, ch
->upstream
, 0);
698 ifjoin_to_noinfo(ch
, true);
700 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
701 * message on RP path upon prune timer expiry.
703 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
705 struct pim_upstream
*parent
=
706 ch
->upstream
->parent
;
708 pim_upstream_update_join_desired(pim_ifp
->pim
,
711 pim_jp_agg_single_upstream_send(&parent
->rpf
,
715 /* from here ch may have been deleted */
721 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
722 struct in_addr upstream
, struct prefix_sg
*sg
,
723 uint8_t source_flags
, int holdtime
)
725 struct pim_upstream
*up
;
726 struct pim_interface
*pim_ifp
= recv_ifp
->info
;
728 /* Upstream (S,G) in Joined state ? */
729 up
= pim_upstream_find(pim_ifp
->pim
, sg
);
732 if (up
->join_state
!= PIM_UPSTREAM_JOINED
)
735 /* Upstream (S,G) in Joined state */
737 if (pim_rpf_addr_is_inaddr_any(&up
->rpf
)) {
738 /* RPF'(S,G) not found */
739 zlog_warn("%s %s: RPF'%s not found", __FILE__
, __func__
,
744 /* upstream directed to RPF'(S,G) ? */
745 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
746 char up_str
[INET_ADDRSTRLEN
];
747 char rpf_str
[PREFIX_STRLEN
];
748 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
749 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
752 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
753 __FILE__
, __func__
, up
->sg_str
, up_str
, rpf_str
,
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
.u
.prefix4
,
766 /* Prune to RPF'(S,G) */
768 if (source_flags
& PIM_RPT_BIT_MASK
) {
769 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
770 /* Prune(*,G) to RPF'(S,G) */
771 pim_upstream_join_timer_decrease_to_t_override(
776 /* Prune(S,G,rpt) to RPF'(S,G) */
777 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
782 /* Prune(S,G) to RPF'(S,G) */
783 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
786 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
787 struct in_addr upstream
, struct prefix_sg
*sg
,
788 uint8_t source_flags
, uint16_t holdtime
)
790 struct pim_interface
*recv_pim_ifp
;
791 int is_local
; /* boolean */
793 recv_pim_ifp
= recv_ifp
->info
;
794 zassert(recv_pim_ifp
);
796 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
801 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
802 char up_str
[INET_ADDRSTRLEN
];
803 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
804 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
805 __func__
, is_join
? "join" : "prune",
806 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
810 * Since recv upstream addr was not directed to our primary
811 * address, check if we should react to it in any way.
813 check_recv_upstream(is_join
, recv_ifp
, upstream
, sg
, source_flags
,
816 return 1; /* non-local */
819 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel
*ch
,
820 struct pim_interface
*pim_ifp
)
822 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
823 PIM_IF_FLAG_UNSET_S_G_RPT(ch
->flags
);
824 /* check if the interface qualifies as an immediate
827 if (pim_upstream_evaluate_join_desired_interface(
830 pim_channel_add_oif(ch
->upstream
->channel_oil
,
832 PIM_OIF_FLAG_PROTO_PIM
,
834 pim_upstream_update_join_desired(pim_ifp
->pim
,
840 void pim_ifchannel_join_add(struct interface
*ifp
, struct in_addr neigh_addr
,
841 struct in_addr upstream
, struct prefix_sg
*sg
,
842 uint8_t source_flags
, uint16_t holdtime
)
844 struct pim_interface
*pim_ifp
;
845 struct pim_ifchannel
*ch
;
847 if (nonlocal_upstream(1 /* join */, ifp
, upstream
, sg
, source_flags
,
852 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
853 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
856 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
858 Transitions from "I am Assert Loser" State
860 Receive Join(S,G) on Interface I
862 We receive a Join(S,G) that has the Upstream Neighbor Address
863 field set to my primary IP address on interface I. The action is
864 to transition to NoInfo state, delete this (S,G) assert state
865 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
868 Notice: The nonlocal_upstream() test above ensures the upstream
869 address of the join message is our primary address.
871 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
872 char neigh_str
[INET_ADDRSTRLEN
];
873 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
875 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
876 __func__
, ch
->sg_str
, neigh_str
, 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 zassert(!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
:
953 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
954 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
955 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
956 pim_ifchannel_ifjoin_switch(__func__
, ch
,
959 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
962 case PIM_IFJOIN_PRUNE_TMP
:
964 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
968 if (holdtime
!= 0xFFFF) {
969 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
970 holdtime
, &ch
->t_ifjoin_expiry_timer
);
974 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
975 struct prefix_sg
*sg
, uint8_t source_flags
,
978 struct pim_ifchannel
*ch
;
979 struct pim_interface
*pim_ifp
;
980 int jp_override_interval_msec
;
982 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
987 ch
= pim_ifchannel_find(ifp
, sg
);
988 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
989 if (PIM_DEBUG_PIM_TRACE
)
991 "%s: Received prune with no relevant ifchannel %s%s state: %d",
992 __func__
, ifp
->name
, pim_str_sg_dump(sg
),
997 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
998 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
1000 pim_ifp
= ifp
->info
;
1002 switch (ch
->ifjoin_state
) {
1003 case PIM_IFJOIN_NOINFO
:
1004 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1005 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
1006 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
1008 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1009 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1010 jp_override_interval_msec
=
1011 pim_if_jp_override_interval_msec(ifp
);
1013 jp_override_interval_msec
=
1014 0; /* schedule to expire immediately */
1015 /* If we called ifjoin_prune() directly instead, care
1017 be taken not to use "ch" afterwards since it would be
1020 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1021 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1022 thread_add_timer_msec(
1023 router
->master
, on_ifjoin_prune_pending_timer
,
1024 ch
, jp_override_interval_msec
,
1025 &ch
->t_ifjoin_prune_pending_timer
);
1026 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1028 &ch
->t_ifjoin_expiry_timer
);
1029 pim_upstream_update_join_desired(pim_ifp
->pim
,
1033 case PIM_IFJOIN_PRUNE_PENDING
:
1036 case PIM_IFJOIN_JOIN
:
1037 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1039 pim_ifchannel_ifjoin_switch(__func__
, ch
,
1040 PIM_IFJOIN_PRUNE_PENDING
);
1042 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1043 jp_override_interval_msec
=
1044 pim_if_jp_override_interval_msec(ifp
);
1046 jp_override_interval_msec
=
1047 0; /* schedule to expire immediately */
1048 /* If we called ifjoin_prune() directly instead, care should
1049 be taken not to use "ch" afterwards since it would be
1051 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1052 thread_add_timer_msec(router
->master
,
1053 on_ifjoin_prune_pending_timer
, ch
,
1054 jp_override_interval_msec
,
1055 &ch
->t_ifjoin_prune_pending_timer
);
1057 case PIM_IFJOIN_PRUNE
:
1058 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1059 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1060 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1062 &ch
->t_ifjoin_expiry_timer
);
1065 case PIM_IFJOIN_PRUNE_TMP
:
1066 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1067 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1068 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1069 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1071 &ch
->t_ifjoin_expiry_timer
);
1074 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1075 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1076 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1077 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1078 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1080 &ch
->t_ifjoin_expiry_timer
);
1086 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1087 struct prefix_sg
*sg
, bool is_vxlan
)
1089 struct pim_ifchannel
*ch
, *starch
;
1090 struct pim_interface
*pim_ifp
;
1091 struct pim_instance
*pim
;
1094 /* PIM enabled on interface? */
1095 pim_ifp
= ifp
->info
;
1097 if (PIM_DEBUG_EVENTS
)
1098 zlog_debug("%s:%s Expected pim interface setup for %s",
1099 __func__
, pim_str_sg_dump(sg
), ifp
->name
);
1103 if (!PIM_IF_TEST_PIM(pim_ifp
->options
)) {
1104 if (PIM_DEBUG_EVENTS
)
1106 "%s:%s PIM is not configured on this interface %s",
1107 __func__
, pim_str_sg_dump(sg
), ifp
->name
);
1113 /* skip (*,G) ch creation if G is of type SSM */
1114 if (sg
->src
.s_addr
== INADDR_ANY
) {
1115 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1116 if (PIM_DEBUG_PIM_EVENTS
)
1118 "%s: local membership (S,G)=%s ignored as group is SSM",
1119 __func__
, pim_str_sg_dump(sg
));
1124 /* vxlan term mroutes use ipmr-lo as local member to
1125 * pull down multicast vxlan tunnel traffic
1127 up_flags
= is_vxlan
? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
:
1128 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
;
1129 ch
= pim_ifchannel_add(ifp
, sg
, 0, up_flags
);
1131 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1133 if (sg
->src
.s_addr
== INADDR_ANY
) {
1134 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1135 struct pim_upstream
*child
;
1136 struct listnode
*up_node
;
1140 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1141 if (PIM_DEBUG_EVENTS
)
1142 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1143 __FILE__
, __func__
, child
->sg_str
,
1144 ifp
->name
, up
->sg_str
);
1146 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1147 if (pim_upstream_evaluate_join_desired_interface(
1148 child
, ch
, starch
)) {
1149 pim_channel_add_oif(child
->channel_oil
, ifp
,
1150 PIM_OIF_FLAG_PROTO_STAR
,
1152 pim_upstream_update_join_desired(pim
, child
);
1156 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1157 if (pim
->spt
.plist
) {
1158 struct prefix_list
*plist
= prefix_list_lookup(
1159 AFI_IP
, pim
->spt
.plist
);
1162 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1163 g
.u
.prefix4
= up
->sg
.grp
;
1165 if (prefix_list_apply(plist
, &g
)
1167 pim_channel_add_oif(
1168 up
->channel_oil
, pim
->regiface
,
1169 PIM_OIF_FLAG_PROTO_IGMP
,
1174 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1175 PIM_OIF_FLAG_PROTO_IGMP
,
1182 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1183 struct prefix_sg
*sg
)
1185 struct pim_ifchannel
*starch
, *ch
, *orig
;
1186 struct pim_interface
*pim_ifp
;
1188 /* PIM enabled on interface? */
1189 pim_ifp
= ifp
->info
;
1192 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1195 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1198 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1200 if (sg
->src
.s_addr
== INADDR_ANY
) {
1201 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1202 struct pim_upstream
*child
;
1203 struct listnode
*up_node
, *up_nnode
;
1207 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1208 struct channel_oil
*c_oil
= child
->channel_oil
;
1209 struct pim_ifchannel
*chchannel
=
1210 pim_ifchannel_find(ifp
, &child
->sg
);
1212 pim_ifp
= ifp
->info
;
1214 if (PIM_DEBUG_EVENTS
)
1215 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1216 __FILE__
, __func__
, up
->sg_str
,
1217 ifp
->name
, child
->sg_str
);
1219 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1221 * If the S,G has no if channel and the c_oil still
1222 * has output here then the *,G was supplying the
1224 * if channel. So remove it.
1226 if (!pim_upstream_evaluate_join_desired_interface(
1227 child
, ch
, starch
) ||
1229 c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])) {
1230 pim_channel_del_inherited_oif(c_oil
, ifp
,
1234 /* Child node removal/ref count-- will happen as part of
1235 * parent' delete_no_info */
1238 delete_on_noinfo(orig
);
1241 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1243 int old_couldassert
=
1244 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1245 int new_couldassert
=
1246 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1248 if (new_couldassert
== old_couldassert
)
1251 if (PIM_DEBUG_PIM_EVENTS
) {
1252 char src_str
[INET_ADDRSTRLEN
];
1253 char grp_str
[INET_ADDRSTRLEN
];
1254 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1255 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1256 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1257 __func__
, src_str
, grp_str
, ch
->interface
->name
,
1258 old_couldassert
, new_couldassert
);
1261 if (new_couldassert
) {
1262 /* CouldAssert(S,G,I) switched from false to true */
1263 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1265 /* CouldAssert(S,G,I) switched from true to false */
1266 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1268 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1269 assert_action_a4(ch
);
1273 pim_ifchannel_update_my_assert_metric(ch
);
1277 my_assert_metric may be affected by:
1280 pim_ifp->primary_address
1281 rpf->source_nexthop.mrib_metric_preference;
1282 rpf->source_nexthop.mrib_route_metric;
1284 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1286 struct pim_assert_metric my_metric_new
=
1287 pim_macro_ch_my_assert_metric_eval(ch
);
1289 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1292 if (PIM_DEBUG_PIM_EVENTS
) {
1293 char src_str
[INET_ADDRSTRLEN
];
1294 char grp_str
[INET_ADDRSTRLEN
];
1295 char old_addr_str
[INET_ADDRSTRLEN
];
1296 char new_addr_str
[INET_ADDRSTRLEN
];
1297 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1298 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1299 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1300 old_addr_str
, sizeof(old_addr_str
));
1301 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1302 new_addr_str
, sizeof(new_addr_str
));
1304 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1305 __func__
, src_str
, grp_str
, ch
->interface
->name
,
1306 ch
->ifassert_my_metric
.rpt_bit_flag
,
1307 ch
->ifassert_my_metric
.metric_preference
,
1308 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1309 my_metric_new
.rpt_bit_flag
,
1310 my_metric_new
.metric_preference
,
1311 my_metric_new
.route_metric
, new_addr_str
);
1314 ch
->ifassert_my_metric
= my_metric_new
;
1316 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1317 &ch
->ifassert_winner_metric
)) {
1318 assert_action_a5(ch
);
1322 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1324 int old_atd
= PIM_FORCE_BOOLEAN(
1325 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1327 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1329 if (new_atd
== old_atd
)
1332 if (PIM_DEBUG_PIM_EVENTS
) {
1333 char src_str
[INET_ADDRSTRLEN
];
1334 char grp_str
[INET_ADDRSTRLEN
];
1335 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1336 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1338 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1339 __func__
, src_str
, grp_str
, ch
->interface
->name
,
1344 /* AssertTrackingDesired(S,G,I) switched from false to true */
1345 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1347 /* AssertTrackingDesired(S,G,I) switched from true to false */
1348 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1350 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1351 assert_action_a5(ch
);
1357 * If we have a new pim interface, check to
1358 * see if any of the pre-existing channels have
1359 * their upstream out that way and turn on forwarding
1360 * for that ifchannel then.
1362 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1364 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1365 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1366 struct interface
*ifp
;
1368 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1369 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1370 struct pim_ifchannel
*ch
;
1375 if (new_pim_ifp
== loop_pim_ifp
)
1378 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1379 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1380 struct pim_upstream
*up
= ch
->upstream
;
1381 if ((!up
->channel_oil
)
1382 && (up
->rpf
.source_nexthop
1383 .interface
== new_ifp
))
1384 pim_forward_start(ch
);
1391 * Downstream per-interface (S,G,rpt) state machine
1392 * states that we need to move (S,G,rpt) items
1393 * into different states at the start of the
1394 * reception of a *,G join as well, when
1395 * we get End of Message
1397 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1400 bool send_upstream_starg
= false;
1401 struct pim_ifchannel
*child
;
1402 struct listnode
*ch_node
, *nch_node
;
1403 struct pim_instance
*pim
=
1404 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1405 struct pim_upstream
*starup
= ch
->upstream
;
1407 if (PIM_DEBUG_PIM_TRACE
)
1409 "%s: %s %s eom: %d join %u", __func__
,
1410 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1411 ch
->sg_str
, eom
, join
);
1415 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1416 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1419 switch (child
->ifjoin_state
) {
1420 case PIM_IFJOIN_NOINFO
:
1421 case PIM_IFJOIN_JOIN
:
1423 case PIM_IFJOIN_PRUNE
:
1425 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1427 case PIM_IFJOIN_PRUNE_PENDING
:
1429 child
->ifjoin_state
=
1430 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1432 case PIM_IFJOIN_PRUNE_TMP
:
1433 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1437 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1438 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1439 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1441 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1442 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1444 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1445 (!pim_upstream_empty_inherited_olist(
1446 child
->upstream
))) {
1447 pim_channel_add_oif(
1448 child
->upstream
->channel_oil
,
1449 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
,
1451 pim_upstream_update_join_desired(pim
,
1454 send_upstream_starg
= true;
1456 delete_on_noinfo(child
);
1461 if (send_upstream_starg
)
1462 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1465 unsigned int pim_ifchannel_hash_key(const void *arg
)
1467 const struct pim_ifchannel
*ch
= arg
;
1469 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);