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 "
144 __func__
, ch
->sg_str
);
145 /* Post Delete only if it is the last Dual-active Interface */
146 if (ch
->upstream
->dualactive_ifchannel_count
== 1) {
147 pim_mlag_up_local_del(pim_ifp
->pim
, ch
->upstream
);
148 PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
149 ch
->upstream
->flags
);
151 ch
->upstream
->dualactive_ifchannel_count
--;
154 if (ch
->upstream
->channel_oil
) {
155 uint32_t mask
= PIM_OIF_FLAG_PROTO_PIM
;
156 if (ch
->upstream
->flags
& PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
157 mask
|= PIM_OIF_FLAG_PROTO_IGMP
;
160 * A S,G RPT channel can have an empty oil, we also
161 * need to take into account the fact that a ifchannel
162 * might have been suppressing a *,G ifchannel from
163 * being inherited. So let's figure out what
164 * needs to be done here
166 if ((ch
->sg
.src
.s_addr
!= INADDR_ANY
) &&
167 pim_upstream_evaluate_join_desired_interface(
168 ch
->upstream
, ch
, ch
->parent
))
169 pim_channel_add_oif(ch
->upstream
->channel_oil
,
171 PIM_OIF_FLAG_PROTO_STAR
,
174 pim_channel_del_oif(ch
->upstream
->channel_oil
,
175 ch
->interface
, mask
, __func__
);
177 * Do we have any S,G's that are inheriting?
178 * Nuke from on high too.
180 if (ch
->upstream
->sources
) {
181 struct pim_upstream
*child
;
182 struct listnode
*up_node
;
184 for (ALL_LIST_ELEMENTS_RO(ch
->upstream
->sources
,
186 pim_channel_del_inherited_oif(
194 * When this channel is removed
195 * we need to find all our children
196 * and make sure our pointers are fixed
198 pim_ifchannel_remove_children(ch
);
201 list_delete(&ch
->sources
);
203 listnode_delete(ch
->upstream
->ifchannels
, ch
);
207 /* upstream is common across ifchannels, check if upstream's
208 ifchannel list is empty before deleting upstream_del
209 ref count will take care of it.
211 if (ch
->upstream
->ref_count
> 0)
212 up
= pim_upstream_del(pim_ifp
->pim
, ch
->upstream
, __func__
);
215 if (PIM_DEBUG_PIM_TRACE
)
217 "%s: Avoiding deletion of upstream with ref_count %d "
218 "from ifchannel(%s): %s",
219 __func__
, ch
->upstream
->ref_count
,
220 ch
->interface
->name
, ch
->sg_str
);
225 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
226 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
227 THREAD_OFF(ch
->t_ifassert_timer
);
230 listnode_delete(ch
->parent
->sources
, ch
);
234 RB_REMOVE(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
236 if (PIM_DEBUG_PIM_TRACE
)
237 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__
,
238 ch
->sg_str
, ch
->interface
->name
);
240 XFREE(MTYPE_PIM_IFCHANNEL
, ch
);
243 pim_upstream_update_join_desired(pim_ifp
->pim
, up
);
246 void pim_ifchannel_delete_all(struct interface
*ifp
)
248 struct pim_interface
*pim_ifp
;
249 struct pim_ifchannel
*ch
;
255 while (!RB_EMPTY(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)) {
256 ch
= RB_ROOT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
);
258 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
259 pim_ifchannel_delete(ch
);
263 static void delete_on_noinfo(struct pim_ifchannel
*ch
)
265 if (ch
->local_ifmembership
== PIM_IFMEMBERSHIP_NOINFO
266 && ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
267 && ch
->t_ifjoin_expiry_timer
== NULL
)
268 pim_ifchannel_delete(ch
);
271 void pim_ifchannel_ifjoin_switch(const char *caller
, struct pim_ifchannel
*ch
,
272 enum pim_ifjoin_state new_state
)
274 enum pim_ifjoin_state old_state
= ch
->ifjoin_state
;
275 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
276 struct pim_ifchannel
*child_ch
;
278 if (PIM_DEBUG_PIM_EVENTS
)
280 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
281 ch
->interface
->name
, ch
->sg_str
,
282 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
283 pim_ifchannel_ifjoin_name(new_state
, 0));
286 if (old_state
== new_state
) {
287 if (PIM_DEBUG_PIM_EVENTS
) {
289 "%s calledby %s: non-transition on state %d (%s)",
290 __func__
, caller
, new_state
,
291 pim_ifchannel_ifjoin_name(new_state
, 0));
296 ch
->ifjoin_state
= new_state
;
298 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
299 struct pim_upstream
*up
= ch
->upstream
;
300 struct pim_upstream
*child
;
301 struct listnode
*up_node
;
304 if (ch
->ifjoin_state
== PIM_IFJOIN_NOINFO
) {
305 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
307 struct channel_oil
*c_oil
=
310 if (PIM_DEBUG_PIM_TRACE
)
312 "%s %s: Prune(S,G)=%s from %s",
320 * If the S,G has no if channel and the
322 * has output here then the *,G was
323 * supplying the implied
324 * if channel. So remove it.
326 if (c_oil
->oil
.mfcc_ttls
327 [pim_ifp
->mroute_vif_index
])
328 pim_channel_del_inherited_oif(
329 c_oil
, ch
->interface
,
333 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
334 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
,
336 if (PIM_DEBUG_PIM_TRACE
)
338 "%s %s: Join(S,G)=%s from %s",
343 /* check if the channel can be
344 * inherited into the SG's OIL
346 child_ch
= pim_ifchannel_find(
349 if (pim_upstream_eval_inherit_if(
350 child
, child_ch
, ch
)) {
354 PIM_OIF_FLAG_PROTO_STAR
,
356 pim_upstream_update_join_desired(
357 pim_ifp
->pim
, child
);
363 /* Transition to/from NOINFO ? */
364 if ((old_state
== PIM_IFJOIN_NOINFO
)
365 || (new_state
== PIM_IFJOIN_NOINFO
)) {
367 if (PIM_DEBUG_PIM_EVENTS
) {
368 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
369 ((new_state
== PIM_IFJOIN_NOINFO
) ? "DOWN"
371 ch
->sg_str
, ch
->interface
->name
);
375 Record uptime of state transition to/from NOINFO
377 ch
->ifjoin_creation
= pim_time_monotonic_sec();
379 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
380 pim_ifchannel_update_could_assert(ch
);
381 pim_ifchannel_update_assert_tracking_desired(ch
);
385 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state
,
388 switch (ifjoin_state
) {
389 case PIM_IFJOIN_NOINFO
:
390 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
394 case PIM_IFJOIN_JOIN
:
396 case PIM_IFJOIN_PRUNE
:
397 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
401 case PIM_IFJOIN_PRUNE_PENDING
:
402 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
406 case PIM_IFJOIN_PRUNE_TMP
:
407 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
411 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
412 if (PIM_IF_FLAG_TEST_S_G_RPT(flags
))
418 return "ifjoin_bad_state";
421 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state
)
423 switch (ifassert_state
) {
424 case PIM_IFASSERT_NOINFO
:
426 case PIM_IFASSERT_I_AM_WINNER
:
428 case PIM_IFASSERT_I_AM_LOSER
:
432 return "ifassert_bad_state";
436 RFC 4601: 4.6.5. Assert State Macros
438 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
439 defaults to Infinity when in the NoInfo state.
441 void reset_ifassert_state(struct pim_ifchannel
*ch
)
443 struct in_addr any
= {.s_addr
= INADDR_ANY
};
445 THREAD_OFF(ch
->t_ifassert_timer
);
447 pim_ifassert_winner_set(ch
, PIM_IFASSERT_NOINFO
, any
,
448 router
->infinite_assert_metric
);
451 struct pim_ifchannel
*pim_ifchannel_find(struct interface
*ifp
,
452 struct prefix_sg
*sg
)
454 struct pim_interface
*pim_ifp
;
455 struct pim_ifchannel
*ch
;
456 struct pim_ifchannel lookup
;
461 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
462 __func__
, pim_str_sg_dump(sg
), ifp
->name
);
467 lookup
.interface
= ifp
;
468 ch
= RB_FIND(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, &lookup
);
473 static void ifmembership_set(struct pim_ifchannel
*ch
,
474 enum pim_ifmembership membership
)
476 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
478 if (ch
->local_ifmembership
== membership
)
481 if (PIM_DEBUG_PIM_EVENTS
) {
482 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
483 __func__
, ch
->sg_str
,
484 membership
== PIM_IFMEMBERSHIP_INCLUDE
? "INCLUDE"
486 ch
->interface
->name
);
489 ch
->local_ifmembership
= membership
;
491 pim_upstream_update_join_desired(pim_ifp
->pim
, ch
->upstream
);
492 pim_ifchannel_update_could_assert(ch
);
493 pim_ifchannel_update_assert_tracking_desired(ch
);
497 void pim_ifchannel_membership_clear(struct interface
*ifp
)
499 struct pim_interface
*pim_ifp
;
500 struct pim_ifchannel
*ch
;
505 RB_FOREACH (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
)
506 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
509 void pim_ifchannel_delete_on_noinfo(struct interface
*ifp
)
511 struct pim_interface
*pim_ifp
;
512 struct pim_ifchannel
*ch
, *ch_tmp
;
517 RB_FOREACH_SAFE (ch
, pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch_tmp
)
518 delete_on_noinfo(ch
);
522 * For a given Interface, if we are given a S,G
523 * Find the *,G (If we have it).
524 * If we are passed a *,G, find the *,* ifchannel
527 static struct pim_ifchannel
*pim_ifchannel_find_parent(struct pim_ifchannel
*ch
)
529 struct prefix_sg parent_sg
= ch
->sg
;
530 struct pim_ifchannel
*parent
= NULL
;
533 if ((parent_sg
.src
.s_addr
!= INADDR_ANY
)
534 && (parent_sg
.grp
.s_addr
!= INADDR_ANY
)) {
535 parent_sg
.src
.s_addr
= INADDR_ANY
;
536 parent
= pim_ifchannel_find(ch
->interface
, &parent_sg
);
539 listnode_add(parent
->sources
, ch
);
546 struct pim_ifchannel
*pim_ifchannel_add(struct interface
*ifp
,
547 struct prefix_sg
*sg
,
548 uint8_t source_flags
, int up_flags
)
550 struct pim_interface
*pim_ifp
;
551 struct pim_ifchannel
*ch
;
552 struct pim_upstream
*up
;
554 ch
= pim_ifchannel_find(ifp
, sg
);
560 ch
= XCALLOC(MTYPE_PIM_IFCHANNEL
, sizeof(*ch
));
563 if ((source_flags
& PIM_ENCODE_RPT_BIT
)
564 && !(source_flags
& PIM_ENCODE_WC_BIT
))
565 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
569 pim_str_sg_set(sg
, ch
->sg_str
);
570 ch
->parent
= pim_ifchannel_find_parent(ch
);
571 if (ch
->sg
.src
.s_addr
== INADDR_ANY
) {
572 ch
->sources
= list_new();
574 (int (*)(void *, void *))pim_ifchannel_compare
;
578 pim_ifchannel_find_new_children(ch
);
579 ch
->local_ifmembership
= PIM_IFMEMBERSHIP_NOINFO
;
581 ch
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
582 ch
->t_ifjoin_expiry_timer
= NULL
;
583 ch
->t_ifjoin_prune_pending_timer
= NULL
;
584 ch
->ifjoin_creation
= 0;
586 RB_INSERT(pim_ifchannel_rb
, &pim_ifp
->ifchannel_rb
, ch
);
588 up
= pim_upstream_add(pim_ifp
->pim
, sg
, NULL
, up_flags
, __func__
, ch
);
592 listnode_add_sort(up
->ifchannels
, ch
);
594 ch
->ifassert_my_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
595 ch
->ifassert_winner_metric
= pim_macro_ch_my_assert_metric_eval(ch
);
597 ch
->ifassert_winner
.s_addr
= INADDR_ANY
;
600 ch
->t_ifassert_timer
= NULL
;
601 ch
->ifassert_state
= PIM_IFASSERT_NOINFO
;
602 reset_ifassert_state(ch
);
603 if (pim_macro_ch_could_assert_eval(ch
))
604 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
606 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
608 if (pim_macro_assert_tracking_desired_eval(ch
))
609 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
611 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
614 * advertise MLAG Data to MLAG peer
616 if (PIM_I_am_DualActive(pim_ifp
)) {
617 up
->dualactive_ifchannel_count
++;
618 /* Sync once for upstream */
619 if (up
->dualactive_ifchannel_count
== 1) {
620 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up
->flags
);
621 pim_mlag_up_local_add(pim_ifp
->pim
, up
);
625 "%s: New Dual active if-chnanel is added to upstream:%s "
626 "count:%d, flags:0x%x",
627 __func__
, up
->sg_str
,
628 up
->dualactive_ifchannel_count
, up
->flags
);
631 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_PIM
)
632 PIM_IF_FLAG_SET_PROTO_PIM(ch
->flags
);
634 if (up_flags
== PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
)
635 PIM_IF_FLAG_SET_PROTO_IGMP(ch
->flags
);
637 if (PIM_DEBUG_PIM_TRACE
)
638 zlog_debug("%s: ifchannel %s(%s) is created ", __func__
,
639 ch
->sg_str
, ch
->interface
->name
);
644 static void ifjoin_to_noinfo(struct pim_ifchannel
*ch
, bool ch_del
)
646 pim_forward_stop(ch
, !ch_del
);
647 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_NOINFO
);
649 delete_on_noinfo(ch
);
652 static int on_ifjoin_expiry_timer(struct thread
*t
)
654 struct pim_ifchannel
*ch
;
658 if (PIM_DEBUG_PIM_TRACE
)
659 zlog_debug("%s: ifchannel %s expiry timer", __func__
,
662 ifjoin_to_noinfo(ch
, true);
663 /* ch may have been deleted */
668 static int on_ifjoin_prune_pending_timer(struct thread
*t
)
670 struct pim_ifchannel
*ch
;
671 int send_prune_echo
; /* boolean */
672 struct interface
*ifp
;
673 struct pim_interface
*pim_ifp
;
677 if (PIM_DEBUG_PIM_TRACE
)
679 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
680 __func__
, pim_str_sg_dump(&ch
->sg
),
681 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
));
683 if (ch
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING
) {
686 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch
->flags
)) {
687 /* Send PruneEcho(S,G) ? */
689 (listcount(pim_ifp
->pim_neighbor_list
) > 1);
691 if (send_prune_echo
) {
694 rpf
.source_nexthop
.interface
= ifp
;
695 rpf
.rpf_addr
.u
.prefix4
=
696 pim_ifp
->primary_address
;
697 pim_jp_agg_single_upstream_send(
698 &rpf
, ch
->upstream
, 0);
701 ifjoin_to_noinfo(ch
, true);
703 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
704 * message on RP path upon prune timer expiry.
706 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
708 struct pim_upstream
*parent
=
709 ch
->upstream
->parent
;
711 pim_upstream_update_join_desired(pim_ifp
->pim
,
714 pim_jp_agg_single_upstream_send(&parent
->rpf
,
718 /* from here ch may have been deleted */
724 static void check_recv_upstream(int is_join
, struct interface
*recv_ifp
,
725 struct in_addr upstream
, struct prefix_sg
*sg
,
726 uint8_t source_flags
, int holdtime
)
728 struct pim_upstream
*up
;
729 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 /* upstream directed to RPF'(S,G) ? */
748 if (upstream
.s_addr
!= up
->rpf
.rpf_addr
.u
.prefix4
.s_addr
) {
749 char up_str
[INET_ADDRSTRLEN
];
750 char rpf_str
[PREFIX_STRLEN
];
751 pim_inet4_dump("<up?>", upstream
, up_str
, sizeof(up_str
));
752 pim_addr_dump("<rpf?>", &up
->rpf
.rpf_addr
, rpf_str
,
755 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
756 __FILE__
, __func__
, up
->sg_str
, up_str
, rpf_str
,
760 /* upstream directed to RPF'(S,G) */
763 /* Join(S,G) to RPF'(S,G) */
764 pim_upstream_join_suppress(up
, up
->rpf
.rpf_addr
.u
.prefix4
,
769 /* Prune to RPF'(S,G) */
771 if (source_flags
& PIM_RPT_BIT_MASK
) {
772 if (source_flags
& PIM_WILDCARD_BIT_MASK
) {
773 /* Prune(*,G) to RPF'(S,G) */
774 pim_upstream_join_timer_decrease_to_t_override(
779 /* Prune(S,G,rpt) to RPF'(S,G) */
780 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
785 /* Prune(S,G) to RPF'(S,G) */
786 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up
);
789 static int nonlocal_upstream(int is_join
, struct interface
*recv_ifp
,
790 struct in_addr upstream
, struct prefix_sg
*sg
,
791 uint8_t source_flags
, uint16_t holdtime
)
793 struct pim_interface
*recv_pim_ifp
;
794 int is_local
; /* boolean */
796 recv_pim_ifp
= recv_ifp
->info
;
797 zassert(recv_pim_ifp
);
799 is_local
= (upstream
.s_addr
== recv_pim_ifp
->primary_address
.s_addr
);
804 if (PIM_DEBUG_PIM_TRACE_DETAIL
) {
805 char up_str
[INET_ADDRSTRLEN
];
806 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
807 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
808 __func__
, is_join
? "join" : "prune",
809 pim_str_sg_dump(sg
), up_str
, recv_ifp
->name
);
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
, struct in_addr neigh_addr
,
844 struct in_addr upstream
, struct prefix_sg
*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 char neigh_str
[INET_ADDRSTRLEN
];
876 pim_inet4_dump("<neigh?>", neigh_addr
, neigh_str
,
878 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
879 __func__
, ch
->sg_str
, neigh_str
, ifp
->name
);
881 assert_action_a5(ch
);
887 switch (ch
->ifjoin_state
) {
888 case PIM_IFJOIN_NOINFO
:
889 pim_ifchannel_ifjoin_switch(__func__
, ch
, PIM_IFJOIN_JOIN
);
890 if (pim_macro_chisin_oiflist(ch
)) {
891 pim_upstream_inherited_olist(pim_ifp
->pim
,
893 pim_forward_start(ch
);
896 * If we are going to be a LHR, we need to note it
898 if (ch
->upstream
->parent
&&
899 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
900 ch
->upstream
->parent
->flags
))
901 && !(ch
->upstream
->flags
902 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR
)) {
903 pim_upstream_ref(ch
->upstream
,
904 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
906 pim_upstream_keep_alive_timer_start(
907 ch
->upstream
, pim_ifp
->pim
->keep_alive_time
);
910 case PIM_IFJOIN_JOIN
:
911 zassert(!ch
->t_ifjoin_prune_pending_timer
);
914 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
916 previously received join message with holdtime=0xFFFF.
918 if (ch
->t_ifjoin_expiry_timer
) {
919 unsigned long remain
= thread_timer_remain_second(
920 ch
->t_ifjoin_expiry_timer
);
921 if (remain
> holdtime
) {
923 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
926 Transitions from Join State
928 The (S,G) downstream state machine on
929 interface I remains in
930 Join state, and the Expiry Timer (ET) is
932 maximum of its current value and the HoldTime
934 triggering Join/Prune message.
936 Conclusion: Do not change the ET if the
938 higher than the received join holdtime.
943 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
945 case PIM_IFJOIN_PRUNE
:
946 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
947 pim_ifchannel_ifjoin_switch(__func__
, ch
,
949 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
950 delete_on_noinfo(ch
);
953 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
955 case PIM_IFJOIN_PRUNE_PENDING
:
956 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
957 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
958 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
959 pim_ifchannel_ifjoin_switch(__func__
, ch
,
962 pim_ifchannel_ifjoin_handler(ch
, pim_ifp
);
965 case PIM_IFJOIN_PRUNE_TMP
:
967 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
971 if (holdtime
!= 0xFFFF) {
972 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
, ch
,
973 holdtime
, &ch
->t_ifjoin_expiry_timer
);
977 void pim_ifchannel_prune(struct interface
*ifp
, struct in_addr upstream
,
978 struct prefix_sg
*sg
, uint8_t source_flags
,
981 struct pim_ifchannel
*ch
;
982 struct pim_interface
*pim_ifp
;
983 int jp_override_interval_msec
;
985 if (nonlocal_upstream(0 /* prune */, ifp
, upstream
, sg
, source_flags
,
990 ch
= pim_ifchannel_find(ifp
, sg
);
991 if (!ch
&& !(source_flags
& PIM_ENCODE_RPT_BIT
)) {
992 if (PIM_DEBUG_PIM_TRACE
)
994 "%s: Received prune with no relevant ifchannel %s%s state: %d",
995 __func__
, ifp
->name
, pim_str_sg_dump(sg
),
1000 ch
= pim_ifchannel_add(ifp
, sg
, source_flags
,
1001 PIM_UPSTREAM_FLAG_MASK_SRC_PIM
);
1003 pim_ifp
= ifp
->info
;
1005 switch (ch
->ifjoin_state
) {
1006 case PIM_IFJOIN_NOINFO
:
1007 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1008 if (!(source_flags
& PIM_ENCODE_WC_BIT
))
1009 PIM_IF_FLAG_SET_S_G_RPT(ch
->flags
);
1011 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1012 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1013 jp_override_interval_msec
=
1014 pim_if_jp_override_interval_msec(ifp
);
1016 jp_override_interval_msec
=
1017 0; /* schedule to expire immediately */
1018 /* If we called ifjoin_prune() directly instead, care
1020 be taken not to use "ch" afterwards since it would be
1023 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1024 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1025 thread_add_timer_msec(
1026 router
->master
, on_ifjoin_prune_pending_timer
,
1027 ch
, jp_override_interval_msec
,
1028 &ch
->t_ifjoin_prune_pending_timer
);
1029 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1031 &ch
->t_ifjoin_expiry_timer
);
1032 pim_upstream_update_join_desired(pim_ifp
->pim
,
1036 case PIM_IFJOIN_PRUNE_PENDING
:
1039 case PIM_IFJOIN_JOIN
:
1040 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1042 pim_ifchannel_ifjoin_switch(__func__
, ch
,
1043 PIM_IFJOIN_PRUNE_PENDING
);
1045 if (listcount(pim_ifp
->pim_neighbor_list
) > 1)
1046 jp_override_interval_msec
=
1047 pim_if_jp_override_interval_msec(ifp
);
1049 jp_override_interval_msec
=
1050 0; /* schedule to expire immediately */
1051 /* If we called ifjoin_prune() directly instead, care should
1052 be taken not to use "ch" afterwards since it would be
1054 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1055 thread_add_timer_msec(router
->master
,
1056 on_ifjoin_prune_pending_timer
, ch
,
1057 jp_override_interval_msec
,
1058 &ch
->t_ifjoin_prune_pending_timer
);
1060 case PIM_IFJOIN_PRUNE
:
1061 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1062 THREAD_OFF(ch
->t_ifjoin_prune_pending_timer
);
1063 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1065 &ch
->t_ifjoin_expiry_timer
);
1068 case PIM_IFJOIN_PRUNE_TMP
:
1069 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1070 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE
;
1071 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1072 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1074 &ch
->t_ifjoin_expiry_timer
);
1077 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1078 if (source_flags
& PIM_ENCODE_RPT_BIT
) {
1079 ch
->ifjoin_state
= PIM_IFJOIN_PRUNE_PENDING
;
1080 THREAD_OFF(ch
->t_ifjoin_expiry_timer
);
1081 thread_add_timer(router
->master
, on_ifjoin_expiry_timer
,
1083 &ch
->t_ifjoin_expiry_timer
);
1089 int pim_ifchannel_local_membership_add(struct interface
*ifp
,
1090 struct prefix_sg
*sg
, bool is_vxlan
)
1092 struct pim_ifchannel
*ch
, *starch
;
1093 struct pim_interface
*pim_ifp
;
1094 struct pim_instance
*pim
;
1097 /* PIM enabled on interface? */
1098 pim_ifp
= ifp
->info
;
1100 if (PIM_DEBUG_EVENTS
)
1101 zlog_debug("%s:%s Expected pim interface setup for %s",
1102 __func__
, pim_str_sg_dump(sg
), ifp
->name
);
1106 if (!PIM_IF_TEST_PIM(pim_ifp
->options
)) {
1107 if (PIM_DEBUG_EVENTS
)
1109 "%s:%s PIM is not configured on this interface %s",
1110 __func__
, pim_str_sg_dump(sg
), ifp
->name
);
1116 /* skip (*,G) ch creation if G is of type SSM */
1117 if (sg
->src
.s_addr
== INADDR_ANY
) {
1118 if (pim_is_grp_ssm(pim
, sg
->grp
)) {
1119 if (PIM_DEBUG_PIM_EVENTS
)
1121 "%s: local membership (S,G)=%s ignored as group is SSM",
1122 __func__
, pim_str_sg_dump(sg
));
1127 /* vxlan term mroutes use ipmr-lo as local member to
1128 * pull down multicast vxlan tunnel traffic
1130 up_flags
= is_vxlan
? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM
:
1131 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP
;
1132 ch
= pim_ifchannel_add(ifp
, sg
, 0, up_flags
);
1134 ifmembership_set(ch
, PIM_IFMEMBERSHIP_INCLUDE
);
1136 if (sg
->src
.s_addr
== INADDR_ANY
) {
1137 struct pim_upstream
*up
= pim_upstream_find(pim
, sg
);
1138 struct pim_upstream
*child
;
1139 struct listnode
*up_node
;
1143 for (ALL_LIST_ELEMENTS_RO(up
->sources
, up_node
, child
)) {
1144 if (PIM_DEBUG_EVENTS
)
1145 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1146 __FILE__
, __func__
, child
->sg_str
,
1147 ifp
->name
, up
->sg_str
);
1149 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1150 if (pim_upstream_evaluate_join_desired_interface(
1151 child
, ch
, starch
)) {
1152 pim_channel_add_oif(child
->channel_oil
, ifp
,
1153 PIM_OIF_FLAG_PROTO_STAR
,
1155 pim_upstream_update_join_desired(pim
, child
);
1159 if (pim
->spt
.switchover
== PIM_SPT_INFINITY
) {
1160 if (pim
->spt
.plist
) {
1161 struct prefix_list
*plist
= prefix_list_lookup(
1162 AFI_IP
, pim
->spt
.plist
);
1165 g
.prefixlen
= IPV4_MAX_PREFIXLEN
;
1166 g
.u
.prefix4
= up
->sg
.grp
;
1168 if (prefix_list_apply(plist
, &g
)
1170 pim_channel_add_oif(
1171 up
->channel_oil
, pim
->regiface
,
1172 PIM_OIF_FLAG_PROTO_IGMP
,
1177 pim_channel_add_oif(up
->channel_oil
, pim
->regiface
,
1178 PIM_OIF_FLAG_PROTO_IGMP
,
1185 void pim_ifchannel_local_membership_del(struct interface
*ifp
,
1186 struct prefix_sg
*sg
)
1188 struct pim_ifchannel
*starch
, *ch
, *orig
;
1189 struct pim_interface
*pim_ifp
;
1191 /* PIM enabled on interface? */
1192 pim_ifp
= ifp
->info
;
1195 if (!PIM_IF_TEST_PIM(pim_ifp
->options
))
1198 orig
= ch
= pim_ifchannel_find(ifp
, sg
);
1201 ifmembership_set(ch
, PIM_IFMEMBERSHIP_NOINFO
);
1203 if (sg
->src
.s_addr
== INADDR_ANY
) {
1204 struct pim_upstream
*up
= pim_upstream_find(pim_ifp
->pim
, sg
);
1205 struct pim_upstream
*child
;
1206 struct listnode
*up_node
, *up_nnode
;
1210 for (ALL_LIST_ELEMENTS(up
->sources
, up_node
, up_nnode
, child
)) {
1211 struct channel_oil
*c_oil
= child
->channel_oil
;
1212 struct pim_ifchannel
*chchannel
=
1213 pim_ifchannel_find(ifp
, &child
->sg
);
1215 pim_ifp
= ifp
->info
;
1217 if (PIM_DEBUG_EVENTS
)
1218 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1219 __FILE__
, __func__
, up
->sg_str
,
1220 ifp
->name
, child
->sg_str
);
1222 ch
= pim_ifchannel_find(ifp
, &child
->sg
);
1224 * If the S,G has no if channel and the c_oil still
1225 * has output here then the *,G was supplying the
1227 * if channel. So remove it.
1229 if (!pim_upstream_evaluate_join_desired_interface(
1230 child
, ch
, starch
) ||
1232 c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])) {
1233 pim_channel_del_inherited_oif(c_oil
, ifp
,
1237 /* Child node removal/ref count-- will happen as part of
1238 * parent' delete_no_info */
1241 delete_on_noinfo(orig
);
1244 void pim_ifchannel_update_could_assert(struct pim_ifchannel
*ch
)
1246 int old_couldassert
=
1247 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
));
1248 int new_couldassert
=
1249 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch
));
1251 if (new_couldassert
== old_couldassert
)
1254 if (PIM_DEBUG_PIM_EVENTS
) {
1255 char src_str
[INET_ADDRSTRLEN
];
1256 char grp_str
[INET_ADDRSTRLEN
];
1257 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1258 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1259 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1260 __func__
, src_str
, grp_str
, ch
->interface
->name
,
1261 old_couldassert
, new_couldassert
);
1264 if (new_couldassert
) {
1265 /* CouldAssert(S,G,I) switched from false to true */
1266 PIM_IF_FLAG_SET_COULD_ASSERT(ch
->flags
);
1268 /* CouldAssert(S,G,I) switched from true to false */
1269 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch
->flags
);
1271 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_WINNER
) {
1272 assert_action_a4(ch
);
1276 pim_ifchannel_update_my_assert_metric(ch
);
1280 my_assert_metric may be affected by:
1283 pim_ifp->primary_address
1284 rpf->source_nexthop.mrib_metric_preference;
1285 rpf->source_nexthop.mrib_route_metric;
1287 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel
*ch
)
1289 struct pim_assert_metric my_metric_new
=
1290 pim_macro_ch_my_assert_metric_eval(ch
);
1292 if (pim_assert_metric_match(&my_metric_new
, &ch
->ifassert_my_metric
))
1295 if (PIM_DEBUG_PIM_EVENTS
) {
1296 char src_str
[INET_ADDRSTRLEN
];
1297 char grp_str
[INET_ADDRSTRLEN
];
1298 char old_addr_str
[INET_ADDRSTRLEN
];
1299 char new_addr_str
[INET_ADDRSTRLEN
];
1300 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1301 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1302 pim_inet4_dump("<old_addr?>", ch
->ifassert_my_metric
.ip_address
,
1303 old_addr_str
, sizeof(old_addr_str
));
1304 pim_inet4_dump("<new_addr?>", my_metric_new
.ip_address
,
1305 new_addr_str
, sizeof(new_addr_str
));
1307 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1308 __func__
, src_str
, grp_str
, ch
->interface
->name
,
1309 ch
->ifassert_my_metric
.rpt_bit_flag
,
1310 ch
->ifassert_my_metric
.metric_preference
,
1311 ch
->ifassert_my_metric
.route_metric
, old_addr_str
,
1312 my_metric_new
.rpt_bit_flag
,
1313 my_metric_new
.metric_preference
,
1314 my_metric_new
.route_metric
, new_addr_str
);
1317 ch
->ifassert_my_metric
= my_metric_new
;
1319 if (pim_assert_metric_better(&ch
->ifassert_my_metric
,
1320 &ch
->ifassert_winner_metric
)) {
1321 assert_action_a5(ch
);
1325 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel
*ch
)
1327 int old_atd
= PIM_FORCE_BOOLEAN(
1328 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch
->flags
));
1330 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch
));
1332 if (new_atd
== old_atd
)
1335 if (PIM_DEBUG_PIM_EVENTS
) {
1336 char src_str
[INET_ADDRSTRLEN
];
1337 char grp_str
[INET_ADDRSTRLEN
];
1338 pim_inet4_dump("<src?>", ch
->sg
.src
, src_str
, sizeof(src_str
));
1339 pim_inet4_dump("<grp?>", ch
->sg
.grp
, grp_str
, sizeof(grp_str
));
1341 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1342 __func__
, src_str
, grp_str
, ch
->interface
->name
,
1347 /* AssertTrackingDesired(S,G,I) switched from false to true */
1348 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1350 /* AssertTrackingDesired(S,G,I) switched from true to false */
1351 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch
->flags
);
1353 if (ch
->ifassert_state
== PIM_IFASSERT_I_AM_LOSER
) {
1354 assert_action_a5(ch
);
1360 * If we have a new pim interface, check to
1361 * see if any of the pre-existing channels have
1362 * their upstream out that way and turn on forwarding
1363 * for that ifchannel then.
1365 void pim_ifchannel_scan_forward_start(struct interface
*new_ifp
)
1367 struct pim_interface
*new_pim_ifp
= new_ifp
->info
;
1368 struct pim_instance
*pim
= new_pim_ifp
->pim
;
1369 struct interface
*ifp
;
1371 FOR_ALL_INTERFACES (pim
->vrf
, ifp
) {
1372 struct pim_interface
*loop_pim_ifp
= ifp
->info
;
1373 struct pim_ifchannel
*ch
;
1378 if (new_pim_ifp
== loop_pim_ifp
)
1381 RB_FOREACH (ch
, pim_ifchannel_rb
, &loop_pim_ifp
->ifchannel_rb
) {
1382 if (ch
->ifjoin_state
== PIM_IFJOIN_JOIN
) {
1383 struct pim_upstream
*up
= ch
->upstream
;
1384 if ((!up
->channel_oil
)
1385 && (up
->rpf
.source_nexthop
1386 .interface
== new_ifp
))
1387 pim_forward_start(ch
);
1394 * Downstream per-interface (S,G,rpt) state machine
1395 * states that we need to move (S,G,rpt) items
1396 * into different states at the start of the
1397 * reception of a *,G join as well, when
1398 * we get End of Message
1400 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel
*ch
, int eom
,
1403 bool send_upstream_starg
= false;
1404 struct pim_ifchannel
*child
;
1405 struct listnode
*ch_node
, *nch_node
;
1406 struct pim_instance
*pim
=
1407 ((struct pim_interface
*)ch
->interface
->info
)->pim
;
1408 struct pim_upstream
*starup
= ch
->upstream
;
1410 if (PIM_DEBUG_PIM_TRACE
)
1412 "%s: %s %s eom: %d join %u", __func__
,
1413 pim_ifchannel_ifjoin_name(ch
->ifjoin_state
, ch
->flags
),
1414 ch
->sg_str
, eom
, join
);
1418 for (ALL_LIST_ELEMENTS(ch
->sources
, ch_node
, nch_node
, child
)) {
1419 if (!PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
))
1422 switch (child
->ifjoin_state
) {
1423 case PIM_IFJOIN_NOINFO
:
1424 case PIM_IFJOIN_JOIN
:
1426 case PIM_IFJOIN_PRUNE
:
1428 child
->ifjoin_state
= PIM_IFJOIN_PRUNE_TMP
;
1430 case PIM_IFJOIN_PRUNE_PENDING
:
1432 child
->ifjoin_state
=
1433 PIM_IFJOIN_PRUNE_PENDING_TMP
;
1435 case PIM_IFJOIN_PRUNE_TMP
:
1436 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
1440 if (child
->ifjoin_state
== PIM_IFJOIN_PRUNE_PENDING_TMP
)
1441 THREAD_OFF(child
->t_ifjoin_prune_pending_timer
);
1442 THREAD_OFF(child
->t_ifjoin_expiry_timer
);
1444 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
1445 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
1447 if ((I_am_RP(pim
, child
->sg
.grp
)) &&
1448 (!pim_upstream_empty_inherited_olist(
1449 child
->upstream
))) {
1450 pim_channel_add_oif(
1451 child
->upstream
->channel_oil
,
1452 ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
,
1454 pim_upstream_update_join_desired(pim
,
1457 send_upstream_starg
= true;
1459 delete_on_noinfo(child
);
1464 if (send_upstream_starg
)
1465 pim_jp_agg_single_upstream_send(&starup
->rpf
, starup
, true);
1468 unsigned int pim_ifchannel_hash_key(const void *arg
)
1470 const struct pim_ifchannel
*ch
= arg
;
1472 return jhash_2words(ch
->sg
.src
.s_addr
, ch
->sg
.grp
.s_addr
, 0);